/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2007  Warzone Resurrection Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * IntDisplay.c
 *
 * Callback and display functions for interface.
 *
 */
#include "lib/framework/frame.h"
#include "lib/framework/strres.h"
#include "lib/framework/math-help.h"

/* Includes direct access to render library */
#include "lib/ivis_common/ivisdef.h"
#include "lib/ivis_common/piestate.h"
#include "lib/ivis_common/piepalette.h"

#include "lib/ivis_common/piemode.h"			// ffs
#include "lib/ivis_common/pieclip.h"			// ffs
#include "lib/ivis_common/pieblitfunc.h"

// FIXME Direct iVis implementation include!
#include "lib/ivis_common/rendmode.h"
#include "lib/ivis_opengl/piematrix.h"

#include "lib/framework/input.h"
#include "lib/widget/slider.h"
#include "lib/widget/editbox.h"
#include "lib/widget/button.h"
#include "lib/widget/label.h"
#include "lib/widget/bar.h"

#include "lib/gamelib/gtime.h"
#include "lib/sound/audio.h"

#include "intdisplay.h"

#include "objects.h"
#include "loop.h"
#include "map.h"
#include "radar.h"

#include "display3d.h"
#include "edit3d.h"
#include "structure.h"
#include "research.h"
#include "function.h"
#include "hci.h"
#include "stats.h"
#include "game.h"
#include "power.h"
#include "order.h"
#include "frontend.h"
#include "intimage.h"
#include "component.h"
#include "console.h"
#include "cmddroid.h"
#include "group.h"
#include "transporter.h"
#include "mission.h"

#include "multiplay.h"

// Is a clickable form widget hilited, either because the cursor is over it or it is flashing.
//
//#define formIsHilite(p) ( (((W_CLICKFORM*)p)->state & WCLICK_HILITE) || (((W_CLICKFORM*)p)->state & WCLICK_FLASHON) )
#define formIsHilite(p) 	(((W_CLICKFORM*)p)->state & WCLICK_HILITE)
#define formIsFlashing(p)	(((W_CLICKFORM*)p)->state & WCLICK_FLASHON)

// Is a button widget hilited, either because the cursor is over it or it is flashing.
//
//#define buttonIsHilite(p) ( (((W_BUTTON*)p)->state & WBUTS_HILITE) ||	(((W_BUTTON*)p)->state & WBUTS_FLASHON) )
#define buttonIsHilite(p) 	(((W_BUTTON*)p)->state & WBUTS_HILITE)
#define buttonIsFlashing(p)  (((W_BUTTON*)p)->state & WBUTS_FLASHON)

#define FORM_OPEN_ANIM_DURATION		(GAME_TICKS_PER_SEC/6) // Time duration for form open/close anims.

//number of pulses in the blip for the radar
#define NUM_PULSES			3

//the loop default value
#define DEFAULT_LOOP		1

static int FormOpenAudioID;	// ID of sfx to play when form opens.
static int FormCloseAudioID; // ID of sfx to play when form closes.
static int FormOpenCount;	// Count used to ensure only one sfx played when two forms opening.
static int FormCloseCount;	// Count used to ensure only one sfx played when two forms closeing.

BASE_STATS *CurrentStatsTemplate = NULL;

#define	DEFAULT_BUTTON_ROTATION (45)
#define BUT_TRANSPORTER_DIST (5000)
#define BUT_TRANSPORTER_SCALE (20)
#define BUT_TRANSPORTER_ALT (-50)

UDWORD ManuPower = 0;	// Power required to manufacture the current item.


// Display surfaces for rendered buttons.
// FIXME: These variables are never, ever referenced directly;
//        they're always used through the below .*Buffers,
//        so rather than declaring them here we should remove
//        these and make them part of the below
static BUTTON_SURFACE TopicSurfaces[NUM_TOPICSURFACES];
static BUTTON_SURFACE ObjectSurfaces[NUM_OBJECTSURFACES];
static BUTTON_SURFACE StatSurfaces[NUM_STATSURFACES];
static BUTTON_SURFACE System0Surfaces[NUM_SYSTEM0SURFACES];

// Working buffers for rendered buttons.
RENDERED_BUTTON System0Buffers[NUM_SYSTEM0BUFFERS];	// References ObjectSurfaces.
RENDERED_BUTTON ObjectBuffers[NUM_OBJECTBUFFERS];	// References ObjectSurfaces.
RENDERED_BUTTON TopicBuffers[NUM_TOPICBUFFERS];		// References TopicSurfaces.
RENDERED_BUTTON StatBuffers[NUM_STATBUFFERS];		// References StatSurfaces.

// Get the first factory assigned to a command droid
STRUCTURE *droidGetCommandFactory(DROID *psDroid);

static SDWORD ButtonDrawXOffset;
static SDWORD ButtonDrawYOffset;


// Set audio IDs for form opening/closing anims.
// Use -1 to dissable audio.
//
void SetFormAudioIDs(int OpenID,int CloseID)
{
	FormOpenAudioID = OpenID;
	FormCloseAudioID = CloseID;
	FormOpenCount = 0;
	FormCloseCount = 0;
}


// Widget callback to update the progress bar in the object stats screen.
//
void intUpdateProgressBar(WIDGET *psWidget, W_CONTEXT *psContext)
{
	BASE_OBJECT			*psObj;
	DROID				*Droid;
	STRUCTURE			*Structure;
	FACTORY				*Manufacture;
	RESEARCH_FACILITY	*Research;
	PLAYER_RESEARCH		*pPlayerRes;
	UDWORD				BuildPoints,Range, BuildPower;
	W_BARGRAPH			*BarGraph = (W_BARGRAPH*)psWidget;

	psObj = (BASE_OBJECT*)BarGraph->pUserData;	// Get the object associated with this widget.

	if (psObj == NULL)
	{
		BarGraph->style |= WIDG_HIDDEN;
		return;
	}

	if (isDead((BASE_OBJECT *)psObj))
	{
		return;
	}

	switch (psObj->type) {
		case OBJ_DROID:						// If it's a droid and...
			Droid = (DROID*)psObj;


			if(DroidIsBuilding(Droid))  // Is it a building.
			{
				ASSERT(Droid->asBits[COMP_CONSTRUCT].nStat, "intUpdateProgressBar: invalid droid type" );

				Structure = DroidGetBuildStructure(Droid);  // Get the structure's building.

				if (Structure)
				{
					//check if have all the power to build yet
					BuildPower = structPowerToBuild(Structure);
					if (Structure->currentPowerAccrued < (SWORD)BuildPower)
					{
						//if not started building show how much power accrued
						//Range = Structure->pStructureType->powerToBuild;
						Range = BuildPower;
						BuildPoints = Structure->currentPowerAccrued;
						//set the colour of the bar to green
						BarGraph->majorCol = WZCOL_GREEN;
						//and change the tool tip
						widgSetTipText((WIDGET*)BarGraph, _("Power Accrued"));
					}
					else
					{
						//show progress of build
						Range =  Structure->pStructureType->buildPoints;	// And how long it takes to build.
						BuildPoints = Structure->currentBuildPts;			// How near to completion.
						//set the colour of the bar to yellow
						BarGraph->majorCol = WZCOL_YELLOW;
						//and change the tool tip
						widgSetTipText((WIDGET*)BarGraph, _("Progress Bar"));
					}
					if (BuildPoints > Range)
					{
						BuildPoints = Range;
					}
					BarGraph->majorSize = (UWORD)PERNUM(WBAR_SCALE,BuildPoints,Range);
					BarGraph->style &= ~WIDG_HIDDEN;
				}
				else
				{
					BarGraph->majorSize = 0;
					BarGraph->style |= WIDG_HIDDEN;
				}
			} else {
				BarGraph->majorSize = 0;
				BarGraph->style |= WIDG_HIDDEN;
			}
			break;

		case OBJ_STRUCTURE:					// If it's a structure and...
			Structure = (STRUCTURE*)psObj;

			if(StructureIsManufacturing(Structure)) {			// Is it manufacturing.
				Manufacture = StructureGetFactory(Structure);
				//check started to build
				if (Manufacture->timeStarted == ACTION_START_TIME)
				{
					//BuildPoints = 0;
					//if not started building show how much power accrued
					Range = ((DROID_TEMPLATE *)Manufacture->psSubject)->powerPoints;
					BuildPoints = Manufacture->powerAccrued;
					//set the colour of the bar to green
					BarGraph->majorCol = WZCOL_GREEN;
					//and change the tool tip
					widgSetTipText((WIDGET*)BarGraph, _("Power Accrued"));
				}
				else
				{
					Range = Manufacture->timeToBuild;
					//set the colour of the bar to yellow
					BarGraph->majorCol = WZCOL_YELLOW;
					//and change the tool tip
					widgSetTipText((WIDGET*)BarGraph, _("Progress Bar"));
					//if on hold need to take it into account
					if (Manufacture->timeStartHold)
					{
						BuildPoints = (gameTime - (Manufacture->timeStarted + (
							gameTime - Manufacture->timeStartHold))) /
							GAME_TICKS_PER_SEC;
					}
					else
					{
						BuildPoints = (gameTime - Manufacture->timeStarted) /
							GAME_TICKS_PER_SEC;
					}
				}
				if (BuildPoints > Range)
				{
					BuildPoints = Range;
				}
				// prevent a division by 0 error
				if (Range == 0 )
				{
					Range = 1;
				}
				BarGraph->majorSize = (UWORD)PERNUM(WBAR_SCALE,BuildPoints,Range);
				BarGraph->style &= ~WIDG_HIDDEN;
			}
			else if(StructureIsResearching(Structure))  // Is it researching.
			{
				Research = StructureGetResearch(Structure);
				pPlayerRes = asPlayerResList[selectedPlayer] +
				             ((RESEARCH *)Research->psSubject - asResearch);
				//this is no good if you change which lab is researching the topic and one lab is faster
				//Range = Research->timeToResearch;
				Range = ((RESEARCH *)((RESEARCH_FACILITY*)Structure->pFunctionality)->psSubject)->researchPoints;
				//check started to research
				if (Research->timeStarted == ACTION_START_TIME)
				{
					//BuildPoints = 0;
					//if not started building show how much power accrued
					Range = ((RESEARCH *)Research->psSubject)->researchPower;
					BuildPoints = Research->powerAccrued;
					//set the colour of the bar to green
					BarGraph->majorCol = WZCOL_GREEN;
					//and change the tool tip
					widgSetTipText((WIDGET*)BarGraph, _("Power Accrued"));
				}
				else
				{
					//set the colour of the bar to yellow
					BarGraph->majorCol = WZCOL_YELLOW;
					//and change the tool tip
					widgSetTipText((WIDGET*)BarGraph, _("Progress Bar"));
					//if on hold need to take it into account
					if (Research->timeStartHold)
					{

						BuildPoints = ((RESEARCH_FACILITY*)Structure->pFunctionality)->
						researchPoints * (gameTime - (Research->timeStarted + (
						gameTime - Research->timeStartHold))) / GAME_TICKS_PER_SEC;

						BuildPoints+= pPlayerRes->currentPoints;
					}
					else
					{

						BuildPoints = ((RESEARCH_FACILITY*)Structure->pFunctionality)->
						researchPoints * (gameTime - Research->timeStarted) / GAME_TICKS_PER_SEC;

						BuildPoints+= pPlayerRes->currentPoints;
					}
				}
				if (BuildPoints > Range)
				{
					BuildPoints = Range;
				}
				// prevent a division by 0 error
				if (Range == 0 )
				{
					Range = 1;
				}
				BarGraph->majorSize = (UWORD)PERNUM(WBAR_SCALE,BuildPoints,Range);
				BarGraph->style &= ~WIDG_HIDDEN;
			} else {
				BarGraph->majorSize = 0;
				BarGraph->style |= WIDG_HIDDEN;
			}

			break;

		default:
			ASSERT( false, "intUpdateProgressBar: invalid object type" );
	}
}


void intUpdateQuantity(WIDGET *psWidget, W_CONTEXT *psContext)
{
	BASE_OBJECT		*psObj;
	STRUCTURE		*Structure;
	DROID_TEMPLATE	*psTemplate;
	W_LABEL			*Label = (W_LABEL*)psWidget;
	UDWORD			Quantity, Remaining;

	psObj = (BASE_OBJECT*)Label->pUserData;	// Get the object associated with this widget.
	Structure = (STRUCTURE*)psObj;

	if( (psObj != NULL) &&
		(psObj->type == OBJ_STRUCTURE) && (StructureIsManufacturing(Structure)) )
	{
		ASSERT(!isDead(psObj),"intUpdateQuantity: object is dead");

		/*Quantity = StructureGetFactory(Structure)->quantity;
		if (Quantity == NON_STOP_PRODUCTION)
		{
			sstrcpy(Label->aText, "*");
		}
		else
		{
			snprintf(Label->aText, sizeof(Label->aText), "%02u", Quantity);
		}*/

		psTemplate = (DROID_TEMPLATE *)StructureGetFactory(Structure)->psSubject;
   		//Quantity = getProductionQuantity(Structure, psTemplate) -
		//					getProductionBuilt(Structure, psTemplate);
        Quantity = getProductionQuantity(Structure, psTemplate);
        Remaining = getProductionBuilt(Structure, psTemplate);
        if (Quantity > Remaining)
        {
            Quantity -= Remaining;
        }
        else
        {
            Quantity = 0;
        }
		if (Quantity)
		{
			snprintf(Label->aText, sizeof(Label->aText), "%02u", Quantity);
		}
		Label->style &= ~WIDG_HIDDEN;
	}
	else
	{
		Label->style |= WIDG_HIDDEN;
	}
}

//callback to display the factory number
void intAddFactoryInc(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_LABEL         *Label = (W_LABEL*)psWidget;
	BASE_OBJECT     *psObj = (BASE_OBJECT*)Label->pUserData;

	// Get the object associated with this widget.
	if (psObj != NULL && !isDead(psObj))
	{
		STRUCTURE	*Structure = (STRUCTURE*)psObj;
		FACTORY		*Factory = (FACTORY *)Structure->pFunctionality;

		ASSERT( (Structure->pStructureType->type == REF_FACTORY ||
			Structure->pStructureType->type == REF_CYBORG_FACTORY ||
			Structure->pStructureType->type == REF_VTOL_FACTORY),
			"intAddFactoryInc: structure is not a factory" );

		snprintf(Label->aText, sizeof(Label->aText), "%u", Factory->psAssemblyPoint->factoryInc + 1);
		Label->style &= ~WIDG_HIDDEN;
	}
	else
	{
		Label->aText[0] = '\0';
		Label->style |= WIDG_HIDDEN;
	}
}

//callback to display the production quantity number for a template
void intAddProdQuantity(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_LABEL				*Label = (W_LABEL*)psWidget;
	BASE_STATS			*psStat = (BASE_STATS *)Label->pUserData;

	// Get the object associated with this widget.
	if (psStat != NULL)
	{
		DROID_TEMPLATE	*psTemplate = (DROID_TEMPLATE *)psStat;
		STRUCTURE	*psStructure = NULL;
		BASE_OBJECT	*psObj = getCurrentSelected();
		UDWORD		quantity = 0, remaining = 0;

		if (psObj != NULL && psObj->type == OBJ_STRUCTURE && !isDead(psObj))
		{
			psStructure = (STRUCTURE *)psObj;
		}

		if (psStructure != NULL && StructIsFactory(psStructure))
		{
			quantity = getProductionQuantity(psStructure, psTemplate);
			remaining = getProductionBuilt(psStructure, psTemplate);
		}

		// now find out how many we have built
		if (quantity > remaining)
		{
			quantity -= remaining;
		}
		else
		{
			quantity = 0;
		}
		if (quantity != 0)
		{
			snprintf(Label->aText, sizeof(Label->aText), "%u", quantity);
			Label->style &= ~WIDG_HIDDEN;
		}
		else
		{
			Label->aText[0] = '\0';
			Label->style |= WIDG_HIDDEN;
		}
	}
}

//callback to display the production loop quantity number for a factory
void intAddLoopQuantity(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_LABEL		*Label = (W_LABEL*)psWidget;
	STRUCTURE	*psStruct = (STRUCTURE *)Label->pUserData;

	//loop depends on the factory
	if (psStruct && psStruct->pFunctionality && !psStruct->died)
	{
		FACTORY		*psFactory = (FACTORY *)psStruct->pFunctionality;

		if (psFactory->quantity == INFINITE_PRODUCTION)
		{
			sstrcpy(Label->aText, "∞");
		}
		else
		{
			snprintf(Label->aText, sizeof(Label->aText), "%02u", psFactory->quantity + DEFAULT_LOOP);
		}
		Label->style &= ~WIDG_HIDDEN;
	}
	else
	{
		//hide the label if no factory
		Label->aText[0] = '\0';
		Label->style |= WIDG_HIDDEN;
	}
}

// callback to update the command droid size label
void intUpdateCommandSize(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_LABEL				*Label = (W_LABEL*)psWidget;
	BASE_OBJECT			*psObj = (BASE_OBJECT*)Label->pUserData;

	// Get the object associated with this widget.
	if (psObj != NULL && !isDead(psObj))
	{
		DROID	*psDroid = (DROID *)psObj;

		ASSERT( psDroid->droidType == DROID_COMMAND,
			"intUpdateCommandSize: droid is not a command droid" );

		snprintf(Label->aText, sizeof(Label->aText), "%d/%d", psDroid->psGroup ? grpNumMembers(psDroid->psGroup) : 0, cmdDroidMaxGroup(psDroid));
		Label->style &= ~WIDG_HIDDEN;
	}
	else
	{
		Label->aText[0] = '\0';
		Label->style |= WIDG_HIDDEN;
	}
}

// callback to update the command droid experience
void intUpdateCommandExp(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_LABEL				*Label = (W_LABEL*)psWidget;
	BASE_OBJECT			*psObj = (BASE_OBJECT*)Label->pUserData;
	SDWORD				i, numStars;

	// Get the object associated with this widget.
	if (psObj != NULL && !isDead(psObj))
	{
		DROID	*psDroid = (DROID *)psObj;

		ASSERT(psObj->type == OBJ_DROID, "intUpdateCommandExp: invalid droid pointer");
		ASSERT(psDroid->droidType == DROID_COMMAND, "intUpdateCommandExp: droid is not a command droid");

		numStars = getDroidLevel(psDroid);
		numStars = (numStars >= 1) ? (numStars - 1) : 0;
		for(i=0; i<numStars; i++)
		{
			Label->aText[i] = '*';
		}
		Label->aText[i] = '\0';
		Label->style &= ~WIDG_HIDDEN;
	}
	else
	{
		Label->aText[0] = '\0';
		Label->style |= WIDG_HIDDEN;
	}
}

// callback to update the command droid factories
void intUpdateCommandFact(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_LABEL				*Label = (W_LABEL*)psWidget;
	BASE_OBJECT			*psObj = (BASE_OBJECT*)Label->pUserData;
	SDWORD				i,cIndex, start;

	// Get the object associated with this widget.
	if (psObj != NULL && !isDead(psObj))
	{
		DROID		*psDroid = (DROID *)psObj;

		ASSERT(psObj->type == OBJ_DROID, "intUpdateCommandFact: invalid droid pointer");
		ASSERT(psDroid->droidType == DROID_COMMAND, "intUpdateCommandFact: droid is not a command droid");

		// see which type of factory this is for
		if (Label->id >= IDOBJ_COUNTSTART && Label->id < IDOBJ_COUNTEND)
		{
			start = DSS_ASSPROD_SHIFT;
		}
		else if (Label->id >= IDOBJ_CMDFACSTART && Label->id < IDOBJ_CMDFACEND)
		{
			start = DSS_ASSPROD_CYBORG_SHIFT;
		}
		else
		{
			start = DSS_ASSPROD_VTOL_SHIFT;
		}

		cIndex = 0;
		for(i=0; i<MAX_FACTORY; i++)
		{
			if ( psDroid->secondaryOrder & (1 << (i + start)) )
			{
				Label->aText[cIndex] = (char) ('0' + i + 1);
				cIndex += 1;
			}
		}
		Label->aText[cIndex] = '\0';
		Label->style &= ~WIDG_HIDDEN;
	}
	else
	{
		Label->aText[0] = '\0';
		Label->style |= WIDG_HIDDEN;
	}
}


#define BARXOFFSET	46

// Widget callback to update and display the power bar.
// !!!!!!!!!!!!!!!!!!!!!!ONLY WORKS ON A SIDEWAYS POWERBAR!!!!!!!!!!!!!!!!!
void intDisplayPowerBar(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_BARGRAPH *BarGraph = (W_BARGRAPH*)psWidget;
	SDWORD		x0,y0;
	SDWORD		Avail,ManPow,realPower;
	SDWORD		Empty;
	SDWORD		BarWidth, textWidth = 0;
	SDWORD		iX,iY;
	static char		szVal[8];

	ManPow = ManuPower / POWERBAR_SCALE;
	Avail = asPower[selectedPlayer].currentPower / POWERBAR_SCALE;
	realPower = asPower[selectedPlayer].currentPower - ManuPower;

	BarWidth = BarGraph->width;
	iV_SetFont(font_regular);
	sprintf( szVal, "%d", realPower );
	textWidth = iV_GetTextWidth( szVal );
	BarWidth -= textWidth;

	if (ManPow > Avail)
	{
		Empty = BarWidth - ManPow;
	}
	else
	{
		Empty = BarWidth - Avail;
	}

	if (Avail > BarWidth)
	{
		ManPow = PERNUM(BarWidth,ManPow,Avail);
		Avail = BarWidth;
		Empty = 0;
	}

	if (ManPow > BarWidth)
	{
		ManPow = BarWidth;
		Avail = 0;
		Empty = 0;
	}

	x0 = xOffset + BarGraph->x;
	y0 = yOffset + BarGraph->y;

	pie_SetDepthBufferStatus(DEPTH_CMP_ALWAYS_WRT_ON);
	pie_SetFogStatus(false);


	iV_DrawImage(IntImages,IMAGE_PBAR_TOP,x0,y0);

	iX = x0 + 3;
	iY = y0 + 9;

	x0 += iV_GetImageWidth(IntImages,IMAGE_PBAR_TOP);

	//fill in the empty section behind text
	if(textWidth > 0)
	{
		iV_DrawImageRect(IntImages,IMAGE_PBAR_EMPTY,
							x0,y0,
							textWidth, iV_GetImageHeight(IntImages,IMAGE_PBAR_EMPTY));
		x0 += textWidth;
	}

	//draw required section
	if (ManPow > Avail)
	{
		//draw the required in red
		iV_DrawImageRect(IntImages,IMAGE_PBAR_USED,
							x0,y0,
							ManPow, iV_GetImageHeight(IntImages,IMAGE_PBAR_USED));
	}
	else
	{
		iV_DrawImageRect(IntImages,IMAGE_PBAR_REQUIRED,
							x0,y0,
							ManPow, iV_GetImageHeight(IntImages,IMAGE_PBAR_REQUIRED));
	}

	x0 += ManPow;

	//draw the available section if any!
	if(Avail-ManPow > 0)
	{
		iV_DrawImageRect(IntImages,IMAGE_PBAR_AVAIL,
							x0,y0,
							Avail-ManPow, iV_GetImageHeight(IntImages,IMAGE_PBAR_AVAIL));

		x0 += Avail-ManPow;
	}

	//fill in the rest with empty section
	if(Empty > 0)
	{
		iV_DrawImageRect(IntImages,IMAGE_PBAR_EMPTY,
							x0,y0,
							Empty, iV_GetImageHeight(IntImages,IMAGE_PBAR_EMPTY));
		x0 += Empty;
	}

	iV_DrawImage(IntImages,IMAGE_PBAR_BOTTOM,x0,y0);

	/* draw text value */
	iV_SetTextColour(WZCOL_TEXT_BRIGHT);
	iV_DrawText( szVal, iX, iY );
}


// Widget callback to display a rendered status button, ie the progress of a manufacturing or
// building task.
//
void intDisplayStatusButton(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_CLICKFORM         *Form = (W_CLICKFORM*)psWidget;
	BASE_OBJECT         *psObj;
	STRUCTURE           *Structure;
	DROID               *Droid;
	BOOL                Down;
	SDWORD              Image;
	BOOL                Hilight = false;
	BASE_STATS          *Stats, *psResGraphic;
	RENDERED_BUTTON     *Buffer = (RENDERED_BUTTON*)Form->pUserData;
	UDWORD              IMDType = 0, compID;
	UDWORD              Player = selectedPlayer;			// changed by AJL for multiplayer.
	void                *Object;
	BOOL	            bOnHold = false;

	OpenButtonRender((UWORD)(xOffset+Form->x), (UWORD)(yOffset+Form->y),(UWORD)Form->width,(UWORD)Form->height);

	Down = Form->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK);

	{
		Hilight = Form->state & WCLICK_HILITE;

		if(Hilight) {
			Buffer->ImdRotation += timeAdjustedIncrement(BUTTONOBJ_ROTSPEED, false);
		}

		Hilight = formIsHilite(Form);	// Hilited or flashing.

		Buffer->State = Form->state;

		Object = NULL;
		Image = -1;
		psObj = (BASE_OBJECT*)Buffer->Data;	// Get the object associated with this widget.

		if (psObj && isDead(psObj))
		{
			// this may catch this horrible crash bug we've been having,
			// who knows?.... Shipping tomorrow, la de da :-)
			psObj = NULL;
			Buffer->Data = NULL;
			intRefreshScreen();
		}

		if(psObj) {
			switch (psObj->type) {
				case OBJ_DROID:						// If it's a droid...
					Droid = (DROID*)psObj;

					if(DroidIsBuilding(Droid)) {
						Structure = DroidGetBuildStructure(Droid);
						if(Structure) {
							Object = Structure;	//(void*)StructureGetIMD(Structure);
							IMDType = IMDTYPE_STRUCTURE;
							RENDERBUTTON_INITIALISED(Buffer);
						}
					} else if (DroidGoingToBuild(Droid)) {
						Stats = DroidGetBuildStats(Droid);
						ASSERT( Stats!=NULL,"intDisplayStatusButton : NULL Stats pointer." );
						Object = (void*)Stats;	//StatGetStructureIMD(Stats,selectedPlayer);
						Player = selectedPlayer;
						IMDType = IMDTYPE_STRUCTURESTAT;
						RENDERBUTTON_INITIALISED(Buffer);
					} else if (orderState(Droid, DORDER_DEMOLISH)) {
						Stats = (BASE_STATS *)structGetDemolishStat();
						ASSERT( Stats!=NULL,"intDisplayStatusButton : NULL Stats pointer." );
						Object = (void*)Stats;
						Player = selectedPlayer;
						IMDType = IMDTYPE_STRUCTURESTAT;
						RENDERBUTTON_INITIALISED(Buffer);
					} else if (Droid->droidType == DROID_COMMAND) {
						Structure = droidGetCommandFactory(Droid);
						if (Structure) {
							Object = Structure;
							IMDType = IMDTYPE_STRUCTURE;
							RENDERBUTTON_INITIALISED(Buffer);
						}
					}
					break;

				case OBJ_STRUCTURE:					// If it's a structure...
					Structure = (STRUCTURE*)psObj;
					switch(Structure->pStructureType->type)
					{
						case REF_FACTORY:
						case REF_CYBORG_FACTORY:
						case REF_VTOL_FACTORY:
							if (StructureIsManufacturing(Structure))
							{
								IMDType = IMDTYPE_DROIDTEMPLATE;
								Object = (void*)FactoryGetTemplate(StructureGetFactory(Structure));
								RENDERBUTTON_INITIALISED(Buffer);
								if (StructureGetFactory(Structure)->timeStartHold)
								{
									bOnHold = true;
								}
							}

							break;

						case REF_RESEARCH:
							if (StructureIsResearching(Structure))
							{
								iIMDShape * shape = Object;
								Stats = (BASE_STATS*)Buffer->Data2;
								if (!Stats)
								{
									break;
								}
	    							if (((RESEARCH_FACILITY *)Structure->pFunctionality)->timeStartHold)
		    						{
			    						bOnHold = true;
				    				}
								StatGetResearchImage(Stats,&Image,&shape, &psResGraphic, false);
								Object = shape;
								if (psResGraphic)
								{
									// we have a Stat associated with this research topic
									if (StatIsStructure(psResGraphic))
									{
										// overwrite the Object pointer
										Object = (void*)psResGraphic;
										Player = selectedPlayer;
										// this defines how the button is drawn
										IMDType = IMDTYPE_STRUCTURESTAT;
									}
									else
									{
										compID = StatIsComponent(psResGraphic);
										if (compID != COMP_UNKNOWN)
										{
											// this defines how the button is drawn
											IMDType = IMDTYPE_COMPONENT;
											// overwrite the Object pointer
											Object = (void*)psResGraphic;
										}
										else
										{
											ASSERT(false, "intDisplayStatsButton:Invalid Stat for research button");
											Object = NULL;
											IMDType = IMDTYPE_RESEARCH;
										}
									}
								}
								else
								{
									// no Stat for this research topic so just use the graphic provided
									// if Object != NULL the there must be a IMD so set the object to
									// equal the Research stat
									if (Object != NULL)
									{
										Object = (void*)Stats;
										IMDType = IMDTYPE_RESEARCH;
									}
								}
								RENDERBUTTON_INITIALISED(Buffer);
							}
							break;
						default:
							break;
					}
					break;

				default:
					ASSERT( false, "intDisplayObjectButton: invalid structure type" );
			}
		} else
		{
			RENDERBUTTON_INITIALISED(Buffer);
		}

		ButtonDrawXOffset = ButtonDrawYOffset = 0;


		// Render the object into the button.
		if(Object) {
			if(Image >= 0) {
				RenderToButton(IntImages,(UWORD)Image,Object,Player,Buffer,Down,IMDType,TOPBUTTON);
			} else {
				RenderToButton(NULL,0,Object,Player,Buffer,Down,IMDType,TOPBUTTON);
			}
		} else if(Image >= 0) {
			RenderImageToButton(IntImages,(UWORD)Image,Buffer,Down,TOPBUTTON);
		} else {
			RenderBlankToButton(Buffer,Down,TOPBUTTON);
		}
	}

	CloseButtonRender();

	//need to flash the button if a factory is on hold production
	if (bOnHold)
	{
		if (((gameTime2/250) % 2) == 0)
		{
			iV_DrawImage(IntImages,IMAGE_BUT0_DOWN,xOffset+Form->x,yOffset+Form->y);
		}
		else
		{
			iV_DrawImage(IntImages,IMAGE_BUT_HILITE,xOffset+Form->x,yOffset+Form->y);
		}
	}
	else
	{
		if (Hilight)
		{

			iV_DrawImage(IntImages,IMAGE_BUT_HILITE,xOffset+Form->x,yOffset+Form->y);
		}
	}
}


// Widget callback to display a rendered object button.
//
void intDisplayObjectButton(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_CLICKFORM *Form = (W_CLICKFORM*)psWidget;
	BASE_OBJECT *psObj;
	BOOL Down;
	BOOL Hilight = false;
	RENDERED_BUTTON *Buffer = (RENDERED_BUTTON*)Form->pUserData;
	UDWORD IMDType = 0;
	void *Object;

	OpenButtonRender((UWORD)(xOffset+Form->x), (UWORD)(yOffset+Form->y),(UWORD)Form->width,(UWORD)(Form->height+9));

	Down = Form->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK);

	{
		Hilight = Form->state & WCLICK_HILITE;

		if(Hilight) {
			Buffer->ImdRotation += timeAdjustedIncrement(BUTTONOBJ_ROTSPEED, false);
		}

		Hilight = formIsHilite(Form);	// Hilited or flashing.

		Buffer->State = Form->state;

		Object = NULL;
		psObj = (BASE_OBJECT*)Buffer->Data;	// Get the object associated with this widget.

		if (psObj && isDead(psObj))
		{
			// this may catch this horrible crash bug we've been having,
			// who knows?.... Shipping tomorrow, la de da :-)
			psObj = NULL;
			Buffer->Data = NULL;
			intRefreshScreen();
		}

		if(psObj) {
			switch (psObj->type) {
				case OBJ_DROID:						// If it's a droid...
					IMDType = IMDTYPE_DROID;
					Object = (void*)psObj;
					break;

				case OBJ_STRUCTURE:					// If it's a structure...
					IMDType = IMDTYPE_STRUCTURE;
					Object = (void*)psObj;
					break;

				default:
					ASSERT( false, "intDisplayStatusButton: invalid structure type" );
			}
		}

		ButtonDrawXOffset = ButtonDrawYOffset = 0;


		if(Object) {
			RenderToButton(NULL,0,Object,selectedPlayer,Buffer,Down,IMDType,BTMBUTTON);	// ajl, changed from 0 to selectedPlayer
		} else {
			RenderBlankToButton(Buffer,Down,BTMBUTTON);
		}


		RENDERBUTTON_INITIALISED(Buffer);
	}

	CloseButtonRender();

	if (Hilight)
	{

		iV_DrawImage(IntImages,IMAGE_BUTB_HILITE,xOffset+Form->x,yOffset+Form->y);
	}
}


// Widget callback to display a rendered stats button, ie the job selection window buttons.
//
void intDisplayStatsButton(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_CLICKFORM     *Form = (W_CLICKFORM*)psWidget;
	BASE_STATS      *Stat, *psResGraphic;
	BOOL            Down;
	SDWORD          Image, compID;
	BOOL            Hilight = false;
	RENDERED_BUTTON *Buffer = (RENDERED_BUTTON*)Form->pUserData;
	UDWORD          IMDType = 0;
	UDWORD          Player = selectedPlayer;		// ajl, changed for multiplayer (from 0)
	void            *Object;

	OpenButtonRender((UWORD)(xOffset+Form->x), (UWORD)(yOffset+Form->y),(UWORD)Form->width,(UWORD)Form->height);

	Down = Form->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK);

	{

		Hilight = Form->state & WCLICK_HILITE;

		if(Hilight) {
			Buffer->ImdRotation += timeAdjustedIncrement(BUTTONOBJ_ROTSPEED, false);
		}

		Hilight = formIsHilite(Form);

		Buffer->State = Form->state;

		Object = NULL;
		Image = -1;

		Stat = (BASE_STATS*)Buffer->Data;

		ButtonDrawXOffset = ButtonDrawYOffset = 0;

		if(Stat)
		{
			if(StatIsStructure(Stat))
			{
				Object = (void*)Stat;
				Player = selectedPlayer;
				IMDType = IMDTYPE_STRUCTURESTAT;
			}
			else if(StatIsTemplate(Stat))
			{
				IMDType = IMDTYPE_DROIDTEMPLATE;
				Object = (void*)Stat;
			}
			else
			{
				//if(StatIsComponent(Stat))
				//{
				//	IMDType = IMDTYPE_COMPONENT;
				//	Shape = StatGetComponentIMD(Stat);
				//}
				compID = StatIsComponent(Stat); // This failes for viper body.
				if (compID != COMP_UNKNOWN)
				{
					IMDType = IMDTYPE_COMPONENT;
					Object = (void*)Stat;	//StatGetComponentIMD(Stat, compID);
				}
				else if(StatIsResearch(Stat))
				{
					iIMDShape * shape = Object;
					/*IMDType = IMDTYPE_RESEARCH;
					StatGetResearchImage(Stat,&Image,(iIMDShape**)&Object,true);
					//if Object != NULL the there must be a IMD so set the object to
					//equal the Research stat
					if (Object != NULL)
					{
						Object = (void*)Stat;
					}*/
                    StatGetResearchImage(Stat,&Image,&shape, &psResGraphic, true);
					Object = shape;
                    if (psResGraphic)
                    {
                        //we have a Stat associated with this research topic
                        if  (StatIsStructure(psResGraphic))
                        {
                            //overwrite the Object pointer
                            Object = (void*)psResGraphic;
				            Player = selectedPlayer;
                            //this defines how the button is drawn
				            IMDType = IMDTYPE_STRUCTURESTAT;
                        }
                        else
                        {
            				compID = StatIsComponent(psResGraphic);
				            if (compID != COMP_UNKNOWN)
				            {
                                //this defines how the button is drawn
					            IMDType = IMDTYPE_COMPONENT;
                                //overwrite the Object pointer
					            Object = (void*)psResGraphic;
				            }
                            else
                            {
                                ASSERT( false,
                                    "intDisplayStatsButton:Invalid Stat for research button" );
                                Object = NULL;
                                IMDType = IMDTYPE_RESEARCH;
                            }
                        }
                    }
                    else
                    {
                        //no Stat for this research topic so just use the graphic provided
                        //if Object != NULL the there must be a IMD so set the object to
                        //equal the Research stat
                        if (Object != NULL)
                        {
                            Object = (void*)Stat;
                            IMDType = IMDTYPE_RESEARCH;
                        }
                    }
				}
			}

			if(Down)
			{
				CurrentStatsTemplate = Stat;
//				CurrentStatsShape = Object;
//				CurrentStatsIndex = (SWORD)IMDIndex;
			}

		}
		else
		{
			IMDType = IMDTYPE_COMPONENT;
			//BLANK button for now - AB 9/1/98
			Object = NULL;
			CurrentStatsTemplate = NULL;
//			CurrentStatsShape = NULL;
//			CurrentStatsIndex = -1;
		}


		if(Object) {
			if(Image >= 0) {
				RenderToButton(IntImages,(UWORD)Image,Object,Player,Buffer,Down,IMDType,TOPBUTTON);
  			} else {
				RenderToButton(NULL,0,Object,Player,Buffer,Down,IMDType,TOPBUTTON);
			}
		} else if(Image >= 0) {
			RenderImageToButton(IntImages,(UWORD)Image,Buffer,Down,TOPBUTTON);
		} else {
			RenderBlankToButton(Buffer,Down,TOPBUTTON);
		}

		RENDERBUTTON_INITIALISED(Buffer);
	}

	CloseButtonRender();

	if (Hilight)
	{

		iV_DrawImage(IntImages,IMAGE_BUT_HILITE,xOffset+Form->x,yOffset+Form->y);
	}
}




void RenderToButton(IMAGEFILE *ImageFile,UWORD ImageID,void *Object,UDWORD Player,
					RENDERED_BUTTON *Buffer,BOOL Down, UDWORD IMDType, UDWORD buttonType)
{
	CreateIMDButton(ImageFile,ImageID,Object,Player,Buffer,Down,IMDType,buttonType);
}

void RenderImageToButton(IMAGEFILE *ImageFile,UWORD ImageID,RENDERED_BUTTON *Buffer,BOOL Down, UDWORD buttonType)
{
	CreateImageButton(ImageFile,ImageID,Buffer,Down,buttonType);
}

void RenderBlankToButton(RENDERED_BUTTON *Buffer,BOOL Down, UDWORD buttonType)
{
	CreateBlankButton(Buffer,Down,buttonType);
}




void AdjustTabFormSize(W_TABFORM *Form,UDWORD *x0,UDWORD *y0,UDWORD *x1,UDWORD *y1)
{
	/* Adjust for where the tabs are */
	if(Form->majorPos == WFORM_TABLEFT) {
		*x0 += Form->tabMajorThickness - Form->tabHorzOffset;
	} else if(Form->minorPos == WFORM_TABLEFT) {
		*x0 += Form->tabMinorThickness - Form->tabHorzOffset;
	}
	if(Form->majorPos == WFORM_TABRIGHT) {
		*x1 -= Form->tabMajorThickness - Form->tabHorzOffset;
	} else if(Form->minorPos == WFORM_TABRIGHT) {
		*x1 -= Form->tabMinorThickness - Form->tabHorzOffset;
	}
	if(Form->majorPos == WFORM_TABTOP) {
		*y0 += Form->tabMajorThickness - Form->tabVertOffset;
	} else if(Form->minorPos == WFORM_TABTOP) {
		*y0 += Form->tabMinorThickness - Form->tabVertOffset;
	}
	if(Form->majorPos == WFORM_TABBOTTOM) {
		*y1 -= Form->tabMajorThickness - Form->tabVertOffset;
	} else if(Form->minorPos == WFORM_TABBOTTOM) {
		*y1 -= Form->tabMinorThickness - Form->tabVertOffset;
	}
}


// Widget callback function to do the open form animation. Doesn't just open Plain Forms!!
//
void intOpenPlainForm(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_TABFORM	*Form = (W_TABFORM*)psWidget;
	UDWORD		Tx0,Ty0,Tx1,Ty1;
	UDWORD		Range;
	UDWORD		Duration;
	UDWORD		APos;
	SDWORD		Ay0,Ay1;

	Tx0 = xOffset+Form->x;
	Ty0 = yOffset+Form->y;
	Tx1 = Tx0 + Form->width;
	Ty1 = Ty0 + Form->height;

	if(Form->animCount == 0) {
		if( (FormOpenAudioID >= 0) && (FormOpenCount == 0) ) {
			audio_PlayTrack(FormOpenAudioID);
			FormOpenCount++;
		}
		Form->Ax0 = (UWORD)Tx0;
		Form->Ax1 = (UWORD)Tx1;
		Form->Ay0 = (UWORD)(Ty0 + (Form->height/2) - 4);
		Form->Ay1 = (UWORD)(Ty0 + (Form->height/2) + 4);
		Form->startTime = gameTime2;
	} else {
		FormOpenCount = 0;
	}

	RenderWindowFrame(FRAME_NORMAL, Form->Ax0, Form->Ay0, Form->Ax1 - Form->Ax0, Form->Ay1 - Form->Ay0);

	Form->animCount++;

	Range = (Form->height/2)-4;
	Duration = (gameTime2 - Form->startTime) << 16 ;
	APos = (Range * (Duration / FORM_OPEN_ANIM_DURATION) ) >> 16;

	Ay0 = Ty0 + (Form->height/2) - 4 - APos;
	Ay1 = Ty0 + (Form->height/2) + 4 + APos;

	if(Ay0 <= (SDWORD)Ty0)
	{
		Ay0 = Ty0;
	}

	if(Ay1 >= (SDWORD)Ty1)
	{
		Ay1 = Ty1;
	}
	Form->Ay0 = (UWORD)Ay0;
	Form->Ay1 = (UWORD)Ay1;

	if((Form->Ay0 == Ty0) && (Form->Ay1 == Ty1))
	{
		if (Form->pUserData != NULL)
		{
			Form->display = (WIDGET_DISPLAY)Form->pUserData;
		}
		else
		{
			//default to display
			Form->display = intDisplayPlainForm;
		}
		Form->disableChildren = false;
		Form->animCount = 0;
	}
}


// Widget callback function to do the close form animation.
//
void intClosePlainForm(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_TABFORM *Form = (W_TABFORM*)psWidget;
	UDWORD Tx0,Ty0,Tx1,Ty1;
	UDWORD Range;
	UDWORD Duration;
	UDWORD APos;

	Tx0 = xOffset+Form->x;
	Tx1 = Tx0 + Form->width;
	Ty0 = yOffset+Form->y + (Form->height/2) - 4;
	Ty1 = yOffset+Form->y + (Form->height/2) + 4;

	if(Form->animCount == 0) {
		if( (FormCloseAudioID >= 0) && (FormCloseCount == 0) ){
			audio_PlayTrack(FormCloseAudioID);
			FormCloseCount++;
		}
		Form->Ax0 = (UWORD)(xOffset+Form->x);
		Form->Ay0 = (UWORD)(yOffset+Form->y);
		Form->Ax1 = (UWORD)(Form->Ax0 + Form->width);
		Form->Ay1 = (UWORD)(Form->Ay0 + Form->height);
		Form->startTime = gameTime2;
	} else {
		FormCloseCount = 0;
	}

	RenderWindowFrame(FRAME_NORMAL, Form->Ax0, Form->Ay0, Form->Ax1 - Form->Ax0, Form->Ay1 - Form->Ay0);

	Form->animCount++;

	Range = (Form->height/2)-4;
	Duration = (gameTime2 - Form->startTime) << 16 ;
	APos = (Range * (Duration / FORM_OPEN_ANIM_DURATION) ) >> 16;

	Form->Ay0 = (UWORD)(yOffset + Form->y + APos);
	Form->Ay1 = (UWORD)(yOffset + Form->y + Form->height - APos);

	if(Form->Ay0 >= Ty0) {
		Form->Ay0 = (UWORD)Ty0;
	}
	if(Form->Ay1 <= Ty1) {
		Form->Ay1 = (UWORD)Ty1;
	}

	if((Form->Ay0 == Ty0) && (Form->Ay1 == Ty1)) {
		Form->pUserData = (void*)1;
		Form->animCount = 0;
	}
}



void intDisplayPlainForm(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_TABFORM *Form = (W_TABFORM*)psWidget;
	UDWORD x0,y0,x1,y1;

	x0 = xOffset+Form->x;
	y0 = yOffset+Form->y;
	x1 = x0 + Form->width;
	y1 = y0 + Form->height;

	RenderWindowFrame(FRAME_NORMAL, x0, y0, x1 - x0, y1 - y0);
}


void intDisplayStatsForm(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_TABFORM *Form = (W_TABFORM*)psWidget;
	UDWORD x0,y0,x1,y1;

	x0 = xOffset+Form->x;
	y0 = yOffset+Form->y;
	x1 = x0 + Form->width;
	y1 = y0 + Form->height;

	AdjustTabFormSize(Form,&x0,&y0,&x1,&y1);

	RenderWindowFrame(FRAME_NORMAL, x0, y0, x1 - x0, y1 - y0);
}


// Display an image for a widget.
//
void intDisplayImage(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	UDWORD x = xOffset+psWidget->x;
	UDWORD y = yOffset+psWidget->y;

	iV_DrawImage(IntImages,psWidget->UserData,x,y);
}


//draws the mission clock - flashes when below a predefined time
void intDisplayMissionClock(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	UDWORD  x = xOffset+psWidget->x;
	UDWORD  y = yOffset+psWidget->y;
    UDWORD  flash;

    //draw the background image
    iV_DrawImage(IntImages,UNPACKDWORD_TRI_B(psWidget->UserData),x,y);
	//need to flash the timer when < 5 minutes remaining, but > 4 minutes
    flash = UNPACKDWORD_TRI_A(psWidget->UserData);
	if (flash && ((gameTime2/250) % 2) == 0)
    {
    	iV_DrawImage(IntImages,UNPACKDWORD_TRI_C(psWidget->UserData),x,y);
	}
}



// Display one of two images depending on if the widget is hilighted by the mouse.
//
void intDisplayImageHilight(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	UDWORD x = xOffset+psWidget->x;
	UDWORD y = yOffset+psWidget->y, flash;
	UWORD ImageID;
	BOOL Hilight = false;

	switch(psWidget->type) {
		case WIDG_FORM:
			Hilight = formIsHilite(psWidget);
//			if( ((W_CLICKFORM*)psWidget)->state & WCLICK_HILITE) ||  {
//				Hilight = true;
//			}
			break;

		case WIDG_BUTTON:
			Hilight = buttonIsHilite(psWidget);
//			if( ((W_BUTTON*)psWidget)->state & WBUTS_HILITE) {
//				Hilight = true;
//			}
			break;

		case WIDG_EDITBOX:
			if( ((W_EDITBOX*)psWidget)->state & WEDBS_HILITE) {
				Hilight = true;
			}
			break;

		case WIDG_SLIDER:
			if( ((W_SLIDER*)psWidget)->state & SLD_HILITE) {
				Hilight = true;
			}
			break;

		default:
			Hilight = false;
	}

	ImageID = UNPACKDWORD_TRI_C(psWidget->UserData);


	//need to flash the button if Full Transporter
    flash = UNPACKDWORD_TRI_A(psWidget->UserData);
	if (flash && psWidget->id == IDTRANS_LAUNCH)
	{
		if (((gameTime2/250) % 2) == 0)
		{
    		iV_DrawImage(IntImages,UNPACKDWORD_TRI_B(psWidget->UserData),x,y);
		}
		else
		{
        	iV_DrawImage(IntImages,ImageID,x,y);
		}
	}
    else
    {
       	iV_DrawImage(IntImages,ImageID,x,y);
        if(Hilight)
        {
	    	iV_DrawImage(IntImages,UNPACKDWORD_TRI_B(psWidget->UserData),x,y);
	    }
    }

}


static void GetButtonState(WIDGET *psWidget,BOOL *Hilight,UDWORD *Down,BOOL *Grey)
{
	switch(psWidget->type) {
		case WIDG_FORM:
			*Hilight = formIsHilite(psWidget);
//			if( ((W_CLICKFORM*)psWidget)->state & WCLICK_HILITE) {
//				Hilight = true;
//			}
			if( ((W_CLICKFORM*)psWidget)->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK)) {
				*Down = 1;
			}
			if( ((W_CLICKFORM*)psWidget)->state & WCLICK_GREY) {
				*Grey = true;
			}
			break;

		case WIDG_BUTTON:
			*Hilight = buttonIsHilite(psWidget);
//			if( ((W_BUTTON*)psWidget)->state & WBUTS_HILITE) {
//				*Hilight = true;
//			}
			if( ((W_BUTTON*)psWidget)->state & (WBUTS_DOWN | WBUTS_LOCKED | WBUTS_CLICKLOCK)) {
				*Down = 1;
			}
			if( ((W_BUTTON*)psWidget)->state & WBUTS_GREY) {
				*Grey = true;
			}
			break;

		case WIDG_EDITBOX:
			if( ((W_EDITBOX*)psWidget)->state & WEDBS_HILITE) {
				*Hilight = true;
			}
			break;

		case WIDG_SLIDER:
			if( ((W_SLIDER*)psWidget)->state & SLD_HILITE) {
				*Hilight = true;
			}
			if( ((W_SLIDER*)psWidget)->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK)) {
				*Down = 1;
			}
			break;

		default:
			*Hilight = false;
	}
}


// Display one of two images depending on if the widget is hilighted by the mouse.
//
void intDisplayButtonHilight(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	UDWORD x = xOffset+psWidget->x;
	UDWORD y = yOffset+psWidget->y;
	BOOL Hilight = false;
	BOOL Grey = false;
	UDWORD Down = 0;
	UWORD ImageID;

	GetButtonState(psWidget,&Hilight,&Down,&Grey);
	if(Grey) {
		ImageID = UNPACKDWORD_TRI_A(psWidget->UserData);
		Hilight = false;
	} else {
		ImageID = UNPACKDWORD_TRI_C(psWidget->UserData) + Down;
	}

	iV_DrawImage(IntImages,ImageID,x,y);
	if(Hilight) {
		iV_DrawImage(IntImages,UNPACKDWORD_TRI_B(psWidget->UserData),x,y);
	}

}

// Flash one of two images depending on if the widget is hilighted by the mouse.
//
void intDisplayButtonFlash(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	UDWORD x = xOffset+psWidget->x;
	UDWORD y = yOffset+psWidget->y;
	BOOL Hilight = false;
	UDWORD Down = 0;
	UWORD ImageID;

	ASSERT( psWidget->type == WIDG_BUTTON,"intDisplayButtonFlash : Not a button" );

	if( ((W_BUTTON*)psWidget)->state & WBUTS_HILITE)
	{
		Hilight = true;
	}

	if( ((W_BUTTON*)psWidget)->state & (WBUTS_DOWN | WBUTS_LOCKED | WBUTS_CLICKLOCK))
	{
		Down = 1;
	}


	if ( Down && ((gameTime2/250) % 2 == 0) )
	{
		ImageID = UNPACKDWORD_TRI_B(psWidget->UserData);
		Hilight = false;
	} else
	{
		ImageID = UNPACKDWORD_TRI_C(psWidget->UserData);
	}

	iV_DrawImage(IntImages,ImageID,x,y);



}

void intDisplayReticuleButton(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	UDWORD	x = xOffset+psWidget->x;
	UDWORD	y = yOffset+psWidget->y;
	BOOL	Hilight = false;
	BOOL	Down = false;
	UBYTE	DownTime = UNPACKDWORD_QUAD_C(psWidget->UserData);
	UBYTE	Index = UNPACKDWORD_QUAD_D(psWidget->UserData);
	UBYTE	flashing = UNPACKDWORD_QUAD_A(psWidget->UserData);
	UBYTE	flashTime = UNPACKDWORD_QUAD_B(psWidget->UserData);
	UWORD	ImageID;

	ASSERT( psWidget->type == WIDG_BUTTON,"intDisplayReticuleButton : Not a button" );


//	iV_DrawImage(IntImages,ImageID,x,y);
	if(((W_BUTTON*)psWidget)->state & WBUTS_GREY) {
		iV_DrawImage(IntImages,IMAGE_RETICULE_GREY,x,y);
		return;
	}


	Down = ((W_BUTTON*)psWidget)->state & (WBUTS_DOWN | WBUTS_CLICKLOCK);
	Hilight = buttonIsHilite(psWidget);

	if(Down)
	{
		if((DownTime < 1) && (Index != IMAGE_CANCEL_UP))
		{
			ImageID = IMAGE_RETICULE_BUTDOWN;	// Do the button flash.
		}
		else
		{
			ImageID = (UWORD)(Index+1);					// It's down.
		}
		DownTime++;
		//stop the reticule from flashing if it was
		flashing = (UBYTE)false;
	}
	else
	{
		//flashing button?
		if (flashing)
		{
			if (((gameTime/250) % 2) == 0)
			{
				ImageID = (UWORD)(Index);//IMAGE_RETICULE_BUTDOWN;//a step in the right direction JPS 27-4-98
			}
			else
			{
				ImageID = (UWORD)(Index+1);
				flashTime = 0;
			}
			flashTime++;
		}
		else
		{
			DownTime = 0;
			ImageID = Index;						// It's up.
		}
	}



	iV_DrawImage(IntImages,ImageID,x,y);

	if(Hilight)
	{
		if (Index == IMAGE_CANCEL_UP)
		{
			iV_DrawImage(IntImages,IMAGE_CANCEL_HILIGHT,x,y);
		}
		else
		{
			iV_DrawImage(IntImages,IMAGE_RETICULE_HILIGHT,x,y);
		}
	}


	psWidget->UserData = PACKDWORD_QUAD(flashTime,flashing,DownTime,Index);
}


void intDisplayTab(WIDGET *psWidget,UDWORD TabType, UDWORD Position,
				   UDWORD Number,BOOL Selected,BOOL Hilight,UDWORD x,UDWORD y,UDWORD Width,UDWORD Height)
{
	TABDEF *Tab = (TABDEF*)psWidget->pUserData;

//	ASSERT( Number < 4,"intDisplayTab : Too many tabs." );
    //Number represents which tab we are on but not interested since they all look the same now - AB 25/01/99
	/*if(Number > 3) {
		Number = 3;
	}*/


	if(TabType == TAB_MAJOR) {
		//iV_DrawImage(IntImages,(UWORD)(Tab->MajorUp+Number),x,y);
        iV_DrawImage(IntImages,(UWORD)Tab->MajorUp,x,y);

		if(Hilight) {
			iV_DrawImage(IntImages,(UWORD)Tab->MajorHilight,x,y);
		} else if(Selected) {
			iV_DrawImage(IntImages,(UWORD)Tab->MajorSelected,x,y);
		}
	} else {
		//iV_DrawImage(IntImages,(UWORD)(Tab->MinorUp+Number),x,y);
        iV_DrawImage(IntImages,(UWORD)(Tab->MinorUp),x,y);

		if(Hilight) {
			iV_DrawImage(IntImages,Tab->MinorHilight,x,y);
		} else if(Selected) {
			iV_DrawImage(IntImages,Tab->MinorSelected,x,y);
		}
	}

}

// Display one of three images depending on if the widget is currently depressed (ah!).
//
void intDisplayButtonPressed(WIDGET *psWidget, UDWORD xOffset,
							 UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_BUTTON	*psButton = (W_BUTTON*)psWidget;
	UDWORD		x = xOffset+psButton->x;
	UDWORD		y = yOffset+psButton->y;
	UBYTE		Hilight = 0;
	UWORD		ImageID;

	if (psButton->state & (WBUTS_DOWN | WBUTS_LOCKED | WBUTS_CLICKLOCK))
	{
		ImageID = UNPACKDWORD_TRI_A(psWidget->UserData);
	}
	else
	{
		ImageID = UNPACKDWORD_TRI_C(psWidget->UserData);
	}

	Hilight = (UBYTE)buttonIsHilite(psButton);
//	if (psButton->state & WBUTS_HILITE)
//	{
//		Hilight = 1;
//	}



	iV_DrawImage(IntImages,ImageID,x,y);
	if (Hilight)
	{
		iV_DrawImage(IntImages,UNPACKDWORD_TRI_B(psWidget->UserData),x,y);
	}


}

// Display DP images depending on factory and if the widget is currently depressed
void intDisplayDPButton(WIDGET *psWidget, UDWORD xOffset,
						UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_BUTTON	*psButton = (W_BUTTON*)psWidget;
	STRUCTURE	*psStruct = (STRUCTURE*)psButton->pUserData;
	UDWORD		x = xOffset+psButton->x;
	UDWORD		y = yOffset+psButton->y;
	UBYTE		hilight = 0, down = 0;
	UWORD		imageID;

	if (psStruct)
	{
		ASSERT( StructIsFactory(psStruct),
			"intDisplayDPButton: structure is not a factory" );

		if (psButton->state & (WBUTS_DOWN | WBUTS_LOCKED | WBUTS_CLICKLOCK))
		{
			down = true;
		}

		hilight = (UBYTE)buttonIsHilite(psButton);
//		if (psButton->state & WBUTS_HILITE)
//		{
//			hilight = true;
//		}

		switch(psStruct->pStructureType->type)
		{
		case REF_FACTORY:
			imageID = IMAGE_FDP_UP;
			break;
		case REF_CYBORG_FACTORY:
			imageID = IMAGE_CDP_UP;
			break;
		case REF_VTOL_FACTORY:
			imageID = IMAGE_VDP_UP;
			break;
		default:
			return;
		}


		iV_DrawImage(IntImages,imageID,x,y);
		if (hilight)
		{
            imageID++;
			iV_DrawImage(IntImages,(UWORD)imageID,x,y);
		}
		else if (down)
		{
            imageID--;
			iV_DrawImage(IntImages,(UWORD)imageID,x,y);
		}

	}
}


void intDisplaySlider(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_SLIDER *Slider = (W_SLIDER*)psWidget;
	UDWORD x = xOffset+psWidget->x;
	UDWORD y = yOffset+psWidget->y;
	SWORD sx;

	iV_DrawImage(IntImages,IMAGE_SLIDER_BACK,x+STAT_SLD_OX,y+STAT_SLD_OY);

	sx = (SWORD)((Slider->width - Slider->barSize)
	 			 * Slider->pos / Slider->numStops);

	iV_DrawImage(IntImages,IMAGE_SLIDER_BUT,x+sx,y-2);
}


/* display highlighted edit box from left, middle and end edit box graphics */
void intDisplayEditBox(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{

	W_EDITBOX	*psEditBox = (W_EDITBOX *) psWidget;
	UWORD		iImageIDLeft, iImageIDMid, iImageIDRight;
	UDWORD		iX, iY, iDX, iXRight;
	UDWORD		iXLeft = xOffset + psWidget->x,
				iYLeft = yOffset + psWidget->y;

	if ( psEditBox->state & WEDBS_HILITE )
	{
		iImageIDLeft  = IMAGE_DES_EDITBOXLEFTH;
		iImageIDMid   = IMAGE_DES_EDITBOXMIDH;
		iImageIDRight = IMAGE_DES_EDITBOXRIGHTH;
	}
	else
	{
		iImageIDLeft  = IMAGE_DES_EDITBOXLEFT;
		iImageIDMid   = IMAGE_DES_EDITBOXMID;
		iImageIDRight = IMAGE_DES_EDITBOXRIGHT;
	}

	/* draw left side of bar */
	iX = iXLeft;
	iY = iYLeft;
	iV_DrawImage( IntImages, iImageIDLeft, iX, iY );

	/* draw middle of bar */
	iX += iV_GetImageWidth( IntImages, iImageIDLeft );
	iDX = iV_GetImageWidth( IntImages, iImageIDMid );
	iXRight = xOffset + psWidget->width - iV_GetImageWidth( IntImages, iImageIDRight );
	while ( iX < iXRight )
	{
		iV_DrawImage( IntImages, iImageIDMid, iX, iY );
		iX += iDX;
	}

	/* draw right side of bar */
	iV_DrawImage( IntImages, iImageIDRight, iXRight, iY );
}



void intDisplayNumber(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_LABEL		*Label = (W_LABEL*)psWidget;
	UDWORD		x = Label->x + xOffset;
	UDWORD		y = Label->y + yOffset;
	UDWORD		Quantity = 1;

	unsigned int i;

	//Quantity depends on the factory
	if (Label->pUserData != NULL)
	{
		STRUCTURE	*psStruct = (STRUCTURE *)Label->pUserData;
		FACTORY		*psFactory = (FACTORY *)psStruct->pFunctionality;

		if (psFactory && !psStruct->died)
		{
			Quantity = psFactory->quantity;
		}
	}

	if(Quantity >= STAT_SLDSTOPS)
	{
		iV_DrawImage(IntImages,IMAGE_SLIDER_INFINITY,x+4,y);
	}
	else
	{
		snprintf(Label->aText, sizeof(Label->aText), "%02u", Quantity);

		for (i = 0; Label->aText[i]; ++i)
		{
			iV_DrawImage(IntImages, (UWORD)(IMAGE_0 + (Label->aText[i] - '0')), x, y);
			x += iV_GetImageWidth(IntImages, (UWORD)(IMAGE_0 + (Label->aText[i]-'0'))) + 1;
		}
	}
}


// Initialise all the surfaces,graphics etc. used by the interface.
//
void intInitialiseGraphics(void)
{
	// Initialise any bitmaps used by the interface.
	imageInitBitmaps();

	// Initialise button surfaces.
	InitialiseButtonData();
}


// Free up all surfaces,graphics etc. used by the interface.
//
void intDeleteGraphics(void)
{
	DeleteButtonData();
}


// Initialise data for interface buttons.
//
void InitialiseButtonData(void)
{
	// Allocate surfaces for rendered buttons.
	UDWORD Width = (iV_GetImageWidth(IntImages,IMAGE_BUT0_UP)+3) & 0xfffffffc;	// Ensure width is whole number of dwords.
	UDWORD Height = iV_GetImageHeight(IntImages,IMAGE_BUT0_UP);
	UDWORD WidthTopic = (iV_GetImageWidth(IntImages,IMAGE_BUTB0_UP)+3) & 0xfffffffc;	// Ensure width is whole number of dwords.
	UDWORD HeightTopic = iV_GetImageHeight(IntImages,IMAGE_BUTB0_UP);

	UDWORD i;

	for(i=0; i<NUM_OBJECTSURFACES; i++) {
		ObjectSurfaces[i].Buffer = (UBYTE*)malloc(Width*Height);
		ASSERT( ObjectSurfaces[i].Buffer!=NULL,"intInitialise : Failed to allocate Object surface" );
		ObjectSurfaces[i].Surface = iV_SurfaceCreate(REND_SURFACE_USR,Width,Height,10,10,ObjectSurfaces[i].Buffer);
		ASSERT( ObjectSurfaces[i].Surface!=NULL,"intInitialise : Failed to create Object surface" );
	}

	for(i=0; i<NUM_OBJECTBUFFERS; i++) {
		RENDERBUTTON_NOTINUSE(&ObjectBuffers[i]);
		ObjectBuffers[i].ButSurf = &ObjectSurfaces[i%NUM_OBJECTSURFACES];
	}

	for(i=0; i<NUM_SYSTEM0SURFACES; i++) {
		System0Surfaces[i].Buffer = (UBYTE*)malloc(Width*Height);
		ASSERT( System0Surfaces[i].Buffer!=NULL,"intInitialise : Failed to allocate System0 surface" );
		System0Surfaces[i].Surface = iV_SurfaceCreate(REND_SURFACE_USR,Width,Height,10,10,System0Surfaces[i].Buffer);
		ASSERT( System0Surfaces[i].Surface!=NULL,"intInitialise : Failed to create System0 surface" );
	}

    for(i=0; i<NUM_SYSTEM0BUFFERS; i++) {
		RENDERBUTTON_NOTINUSE(&System0Buffers[i]);
		System0Buffers[i].ButSurf = &System0Surfaces[i%NUM_SYSTEM0SURFACES];
	}

	for(i=0; i<NUM_TOPICSURFACES; i++) {
		TopicSurfaces[i].Buffer = (UBYTE*)malloc(WidthTopic*HeightTopic);
		ASSERT( TopicSurfaces[i].Buffer!=NULL,"intInitialise : Failed to allocate Topic surface" );
		TopicSurfaces[i].Surface = iV_SurfaceCreate(REND_SURFACE_USR,WidthTopic,HeightTopic,10,10,TopicSurfaces[i].Buffer);
		ASSERT( TopicSurfaces[i].Surface!=NULL,"intInitialise : Failed to create Topic surface" );
	}

	for(i=0; i<NUM_TOPICBUFFERS; i++) {
		RENDERBUTTON_NOTINUSE(&TopicBuffers[i]);
		TopicBuffers[i].ButSurf = &TopicSurfaces[i%NUM_TOPICSURFACES];
	}

	for(i=0; i<NUM_STATSURFACES; i++) {
		StatSurfaces[i].Buffer = (UBYTE*)malloc(Width*Height);
		ASSERT( StatSurfaces[i].Buffer!=NULL,"intInitialise : Failed to allocate Stats surface" );
		StatSurfaces[i].Surface = iV_SurfaceCreate(REND_SURFACE_USR,Width,Height,10,10,StatSurfaces[i].Buffer);
		ASSERT( StatSurfaces[i].Surface!=NULL,"intInitialise : Failed to create Stat surface" );
	}

	for(i=0; i<NUM_STATBUFFERS; i++) {
		RENDERBUTTON_NOTINUSE(&StatBuffers[i]);
		StatBuffers[i].ButSurf = &StatSurfaces[i%NUM_STATSURFACES];
	}
}


void RefreshObjectButtons(void)
{
	UDWORD i;

	for(i=0; i<NUM_OBJECTBUFFERS; i++) {
		RENDERBUTTON_NOTINITIALISED(&ObjectBuffers[i]);
	}
}

void RefreshSystem0Buttons(void)
{
	UDWORD i;

	for(i=0; i<NUM_SYSTEM0BUFFERS; i++) {
		RENDERBUTTON_NOTINITIALISED(&System0Buffers[i]);
	}
}
void RefreshTopicButtons(void)
{
	UDWORD i;

	for(i=0; i<NUM_TOPICBUFFERS; i++) {
		RENDERBUTTON_NOTINITIALISED(&TopicBuffers[i]);
	}
}


void RefreshStatsButtons(void)
{
	UDWORD i;

	for(i=0; i<NUM_STATBUFFERS; i++) {
		RENDERBUTTON_NOTINITIALISED(&StatBuffers[i]);
	}
}


void ClearObjectBuffers(void)
{
	UDWORD i;

	for(i=0; i<NUM_OBJECTBUFFERS; i++) {
		ClearObjectButtonBuffer(i);
	}
}

void ClearTopicBuffers(void)
{
	UDWORD i;

	for(i=0; i<NUM_TOPICBUFFERS; i++) {
		ClearTopicButtonBuffer(i);
	}
}

void ClearObjectButtonBuffer(SDWORD BufferID)
{
	RENDERBUTTON_NOTINITIALISED(&ObjectBuffers[BufferID]);	//  what have I done
	RENDERBUTTON_NOTINUSE(&ObjectBuffers[BufferID]);
	ObjectBuffers[BufferID].Data = NULL;
	ObjectBuffers[BufferID].Data2 = NULL;
	ObjectBuffers[BufferID].ImdRotation = DEFAULT_BUTTON_ROTATION;
}

void ClearTopicButtonBuffer(SDWORD BufferID)
{
	RENDERBUTTON_NOTINITIALISED(&TopicBuffers[BufferID]);	//  what have I done
	RENDERBUTTON_NOTINUSE(&TopicBuffers[BufferID]);
	TopicBuffers[BufferID].Data = NULL;
	TopicBuffers[BufferID].Data2 = NULL;
	TopicBuffers[BufferID].ImdRotation = DEFAULT_BUTTON_ROTATION;
}

SDWORD GetObjectBuffer(void)
{
	SDWORD i;

	for(i=0; i<NUM_OBJECTBUFFERS; i++) {
		if( IsBufferInUse(&ObjectBuffers[i])==false )
		{
			return i;
		}
	}

	return -1;
}

void ClearStatBuffers(void)
{
	UDWORD i;

	for(i=0; i<NUM_STATBUFFERS; i++) {
		RENDERBUTTON_NOTINITIALISED(&StatBuffers[i]);	//  what have I done
		RENDERBUTTON_NOTINUSE(&StatBuffers[i]);
		StatBuffers[i].Data = NULL;
		StatBuffers[i].ImdRotation = DEFAULT_BUTTON_ROTATION;
	}
}

SDWORD GetStatBuffer(void)
{
	SDWORD i;

	for(i=0; i<NUM_STATBUFFERS; i++) {
		if( IsBufferInUse(&StatBuffers[i])==false )
 		{
	 		return i;
		}
	}

	return -1;
}

/*these have been set up for the Transporter - the design screen DOESN'T use them
NB On the PC there are 80!!!!!*/
void ClearSystem0Buffers(void)
{
	UDWORD i;

	for(i=0; i<NUM_SYSTEM0BUFFERS; i++) {
		ClearSystem0ButtonBuffer(i);
	}
}

void ClearSystem0ButtonBuffer(SDWORD BufferID)
{
	RENDERBUTTON_NOTINITIALISED(&System0Buffers[BufferID]);	//  what have I done
	RENDERBUTTON_NOTINUSE(&System0Buffers[BufferID]);
	System0Buffers[BufferID].Data = NULL;
	System0Buffers[BufferID].Data2 = NULL;
	System0Buffers[BufferID].ImdRotation = DEFAULT_BUTTON_ROTATION;
}

SDWORD GetSystem0Buffer(void)
{
	SDWORD i;

	for(i=0; i<NUM_SYSTEM0BUFFERS; i++)
	{
		if( IsBufferInUse(&System0Buffers[i])==false )
		{
			return i;
		}
	}

	return -1;
}


// Free up data for interface buttons.
//
void DeleteButtonData(void)
{
	unsigned int i;

	// Setting all these pointers may, or may not be necessary, but it surely is safe
	// Look above (near the declaration of .*Surfaces) for a detailed description of why this .*Surfaces stuff is bad
	for(i = 0; i < NUM_OBJECTSURFACES; ++i)
	{
		free(ObjectSurfaces[i].Buffer);
		ObjectSurfaces[i].Buffer = NULL;
		iV_SurfaceDestroy(ObjectSurfaces[i].Surface);
	}

	for(i = 0; i < NUM_TOPICSURFACES; ++i)
	{
		free(TopicSurfaces[i].Buffer);
		TopicSurfaces[i].Buffer = NULL;
		iV_SurfaceDestroy(TopicSurfaces[i].Surface);
	}

	for(i = 0; i < NUM_STATSURFACES; ++i)
	{
		free(StatSurfaces[i].Buffer);
		StatSurfaces[i].Buffer = NULL;
		iV_SurfaceDestroy(StatSurfaces[i].Surface);
	}

	for(i = 0; i < NUM_SYSTEM0SURFACES; ++i)
	{
		free(System0Surfaces[i].Buffer);
		System0Surfaces[i].Buffer = NULL;
		iV_SurfaceDestroy(System0Surfaces[i].Surface);
	}
}


static UWORD ButXPos = 0;
static UWORD ButYPos = 0;
static UWORD ButWidth, ButHeight;

void OpenButtonRender(UWORD XPos,UWORD YPos,UWORD Width,UWORD Height)
{
	ButXPos = XPos;
	ButYPos = YPos;
	ButWidth = Width;
	ButHeight = Height;
}

void CloseButtonRender(void)
{
}


// Clear a button bitmap. ( copy the button background ).
//
void ClearButton(BOOL Down,UDWORD Size, UDWORD buttonType)
{
	if(Down)
	{
		pie_ImageFileID(IntImages,(UWORD)(IMAGE_BUT0_DOWN+(buttonType*2)),ButXPos,ButYPos);
	}
	else
	{
		pie_ImageFileID(IntImages,(UWORD)(IMAGE_BUT0_UP+(buttonType*2)),ButXPos,ButYPos);
	}
}

// Create a button by rendering an IMD object into it.
//
void CreateIMDButton(IMAGEFILE *ImageFile, UWORD ImageID, void *Object, UDWORD Player, RENDERED_BUTTON *Buffer, BOOL Down, UDWORD IMDType, UDWORD buttonType)
{
	UDWORD Size;
	Vector3i Rotation, Position, NullVector;
	UDWORD ox,oy;
	BUTTON_SURFACE *ButSurf;
	UDWORD Radius;
	UDWORD basePlateSize;
	SDWORD scale;

	ButSurf = Buffer->ButSurf;

	if(Down) {
		ox = oy = 2;
	} else {
		ox = oy = 0;
	}

	if((IMDType == IMDTYPE_DROID) || (IMDType == IMDTYPE_DROIDTEMPLATE)) {	// The case where we have to render a composite droid.

		if(Down)
		{
			//the top button is smaller than the bottom button
			if (buttonType == TOPBUTTON)
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUT0_DOWN)/2) + ButtonDrawXOffset + 2,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUT0_DOWN)/2) + 2 + 8 + ButtonDrawYOffset);
			}
			else
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUTB0_DOWN)/2) + ButtonDrawXOffset + 2,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUTB0_DOWN)/2) + 2 + 12 + ButtonDrawYOffset);
			}
		}
		else
		{
			//the top button is smaller than the bottom button
			if (buttonType == TOPBUTTON)
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUT0_UP)/2) + ButtonDrawXOffset,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUT0_UP)/2) + 8  + ButtonDrawYOffset);
			}
			else
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUT0_UP)/2) + ButtonDrawXOffset,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUTB0_UP)/2) + 12  + ButtonDrawYOffset);
			}
		}

		if(IMDType == IMDTYPE_DROID)
		{
			Radius = getComponentDroidRadius((DROID*)Object);
		}
		else
		{
			Radius = getComponentDroidTemplateRadius((DROID_TEMPLATE*)Object);
		}

		Size = 2;
		scale = DROID_BUT_SCALE;
		ASSERT( Radius <= 128,"create PIE button big component found" );

		ClearButton(Down, Size, buttonType);

		Rotation.x = -30;
		Rotation.y = (UDWORD) Buffer->ImdRotation;
		Rotation.z = 0;

		NullVector.x = 0;
		NullVector.y = 0;
		NullVector.z = 0;

		if(IMDType == IMDTYPE_DROID)
		{
			if(((DROID*)Object)->droidType == DROID_TRANSPORTER) {
				Position.x = 0;
				Position.y = 0;//BUT_TRANSPORTER_ALT;
				Position.z = BUTTON_DEPTH;
				scale = DROID_BUT_SCALE/2;
			}
			else
			{
				Position.x = Position.y = 0;
				Position.z = BUTTON_DEPTH;
			}
		}
		else//(IMDType == IMDTYPE_DROIDTEMPLATE)
		{
			if(((DROID_TEMPLATE*)Object)->droidType == DROID_TRANSPORTER) {
				Position.x = 0;
				Position.y = 0;//BUT_TRANSPORTER_ALT;
				Position.z = BUTTON_DEPTH;
				scale = DROID_BUT_SCALE/2;
			}
			else
			{
				Position.x = Position.y = 0;
				Position.z = BUTTON_DEPTH;
			}
		}

		//lefthand display droid buttons
		if(IMDType == IMDTYPE_DROID)
		{
			displayComponentButtonObject((DROID*)Object,&Rotation,&Position,true, scale);
		}
		else
		{


			displayComponentButtonTemplate((DROID_TEMPLATE*)Object,&Rotation,&Position,true, scale);
		}
	}
	else
	{	// Just drawing a single IMD.

		if(Down)
		{
			if (buttonType == TOPBUTTON)
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUT0_DOWN)/2) + ButtonDrawXOffset + 2,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUT0_DOWN)/2) + 2 + 8 + ButtonDrawYOffset);
			}
			else
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUTB0_DOWN)/2) + ButtonDrawXOffset + 2,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUTB0_DOWN)/2) + 2 + 12 + ButtonDrawYOffset);
			}
		}
		else
		{
			if (buttonType == TOPBUTTON)
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUT0_UP)/2) + ButtonDrawXOffset,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUT0_UP)/2) + 8  + ButtonDrawYOffset);
			}
			else
			{
				pie_SetGeometricOffset(
					(ButXPos + iV_GetImageWidth(IntImages,IMAGE_BUTB0_UP)/2) + ButtonDrawXOffset,
					(ButYPos + iV_GetImageHeight(IntImages,IMAGE_BUTB0_UP)/2) + 12  + ButtonDrawYOffset);
			}
		}

	// Decide which button grid size to use.
		if(IMDType == IMDTYPE_COMPONENT)
		{
			Radius = getComponentRadius((BASE_STATS*)Object);
			Size = 2;//small structure
			scale = rescaleButtonObject(Radius, COMP_BUT_SCALE, COMPONENT_RADIUS);
			//scale = COMP_BUT_SCALE;
			//ASSERT( Radius <= OBJECT_RADIUS,"Object too big for button - %s",
			//		((BASE_STATS*)Object)->pName );
		}
		else if(IMDType == IMDTYPE_RESEARCH)
		{
			Radius = getResearchRadius((BASE_STATS*)Object);
			if(Radius <= 100)
			{
				Size = 2;//small structure
				scale = rescaleButtonObject(Radius, COMP_BUT_SCALE, COMPONENT_RADIUS);
				//scale = COMP_BUT_SCALE;
			}
			else if(Radius <= 128)
			{
				Size = 2;//small structure
				scale = SMALL_STRUCT_SCALE;
			}
			else if(Radius <= 256)
			{
				Size = 1;//med structure
				scale = MED_STRUCT_SCALE;
			}
			else
			{
				Size = 0;
				scale = LARGE_STRUCT_SCALE;
			}
		}
		else if(IMDType == IMDTYPE_STRUCTURE)
		{
			basePlateSize = getStructureSize((STRUCTURE*)Object);
			if(basePlateSize == 1)
			{
				Size = 2;//small structure
				scale = SMALL_STRUCT_SCALE;
			}
			else if(basePlateSize == 2)
			{
				Size = 1;//med structure
				scale = MED_STRUCT_SCALE;
			}
			else
			{
				Size = 0;
				scale = LARGE_STRUCT_SCALE;
			}
		}
		else if(IMDType == IMDTYPE_STRUCTURESTAT)
		{
			basePlateSize= getStructureStatSize((STRUCTURE_STATS*)Object);
			if(basePlateSize == 1)
			{
				Size = 2;//small structure
				scale = SMALL_STRUCT_SCALE;
			}
			else if(basePlateSize == 2)
			{
				Size = 1;//med structure
				scale = MED_STRUCT_SCALE;
			}
			else
			{
				Size = 0;
				scale = LARGE_STRUCT_SCALE;
			}
		}
		else
		{

			Radius = ((iIMDShape*)Object)->sradius;

			if(Radius <= 128) {
				Size = 2;//small structure
				scale = SMALL_STRUCT_SCALE;
			} else if(Radius <= 256) {
				Size = 1;//med structure
				scale = MED_STRUCT_SCALE;
			} else {
				Size = 0;
				scale = LARGE_STRUCT_SCALE;
			}
		}



		ClearButton(Down,Size, buttonType);

		Rotation.x = -30;
		Rotation.y = (UWORD ) Buffer->ImdRotation;
		Rotation.z = 0;

		NullVector.x = 0;
		NullVector.y = 0;
		NullVector.z = 0;

		Position.x = 0;
		Position.y = 0;
		Position.z = BUTTON_DEPTH; //was 		Position.z = Radius*30;

		if (ImageFile)
		{
			iV_DrawImage(ImageFile,ImageID,ButXPos+ox,ButYPos+oy);
		}

		pie_SetDepthBufferStatus(DEPTH_CMP_LEQ_WRT_ON);

		/* all non droid buttons */
		if(IMDType == IMDTYPE_COMPONENT) {
			displayComponentButton((BASE_STATS*)Object,&Rotation,&Position,true, scale);
		} else if(IMDType == IMDTYPE_RESEARCH) {
			displayResearchButton((BASE_STATS*)Object,&Rotation,&Position,true, scale);
		} else if(IMDType == IMDTYPE_STRUCTURE) {
			displayStructureButton((STRUCTURE*)Object,&Rotation,&Position,true, scale);
		} else if(IMDType == IMDTYPE_STRUCTURESTAT) {
			displayStructureStatButton((STRUCTURE_STATS*)Object,Player,&Rotation,&Position,true, scale);
		} else {
			displayIMDButton((iIMDShape*)Object,&Rotation,&Position,true, scale);
		}

		pie_SetDepthBufferStatus(DEPTH_CMP_ALWAYS_WRT_ON);
	}
}


// Create a button by rendering an image into it.
//
void CreateImageButton(IMAGEFILE *ImageFile,UWORD ImageID,RENDERED_BUTTON *Buffer,BOOL Down, UDWORD buttonType)
{
	UDWORD ox,oy;

	ox = oy = 0;
	/*if(Down)
	{
		ox = oy = 2;
	} */

	ClearButton(Down,0, buttonType);

	iV_DrawImage(ImageFile,ImageID,ButXPos+ox,ButYPos+oy);
//	DrawTransImageSR(Image,ox,oy);
}


// Create a blank button.
//
void CreateBlankButton(RENDERED_BUTTON *Buffer,BOOL Down, UDWORD buttonType)
{
	UDWORD ox,oy;

	if(Down) {
		ox = oy = 1;
	} else {
		ox = oy = 0;
	}

	ClearButton(Down,0, buttonType);

	// Draw a question mark, bit of quick hack this.
	iV_DrawImage(IntImages,IMAGE_QUESTION_MARK,ButXPos+ox+10,ButYPos+oy+3);
}

// Returns true if the droid is currently demolishing something or moving to demolish something.
//
BOOL DroidIsDemolishing(DROID *Droid)
{
	BASE_STATS	*Stats;
	UDWORD x,y;

	//if(droidType(Droid) != DROID_CONSTRUCT) return false;
	if (!(droidType(Droid) == DROID_CONSTRUCT ||
		droidType(Droid) == DROID_CYBORG_CONSTRUCT))
	{
		return false;
	}

	if(orderStateStatsLoc(Droid, DORDER_DEMOLISH,&Stats,&x,&y)) // Moving to demolish location?
	{
		return true;
	}
	else if (orderStateObj(Droid, DORDER_DEMOLISH)) // Is demolishing?
	{
		return true;
	}

	return false;
}

// Returns true if the droid is currently repairing another droid.
BOOL DroidIsRepairing(DROID *Droid)
{
	//if(droidType(Droid) != DROID_REPAIR)
	if (!(droidType(Droid) == DROID_REPAIR
	   || droidType(Droid) == DROID_CYBORG_REPAIR))
	{
		return false;
	}

	if (orderStateObj(Droid, DORDER_DROIDREPAIR))
	{
		return true;
	}

	return false;
}

// Returns true if the droid is currently building something.
//
BOOL DroidIsBuilding(DROID *Droid)
{
	BASE_STATS	*Stats;
	UDWORD x,y;

	//if(droidType(Droid) != DROID_CONSTRUCT) return false;
    if (!(droidType(Droid) == DROID_CONSTRUCT ||
        droidType(Droid) == DROID_CYBORG_CONSTRUCT))
    {
        return false;
    }

	if (orderStateStatsLoc(Droid, DORDER_BUILD, &Stats, &x, &y))
	{
		// Moving to build location?
		return false;
	}
	else if (orderStateObj(Droid, DORDER_BUILD)
	      || orderStateObj(Droid, DORDER_HELPBUILD)) // Is building or helping?
	{
		return true;
	}

	return false;
}


// Returns true if the droid has been ordered build something ( but has'nt started yet )
//
BOOL DroidGoingToBuild(DROID *Droid)
{
	BASE_STATS	*Stats;
	UDWORD x,y;

	//if(droidType(Droid) != DROID_CONSTRUCT) return false;
    if (!(droidType(Droid) == DROID_CONSTRUCT ||
        droidType(Droid) == DROID_CYBORG_CONSTRUCT))
    {
        return false;
    }

	if(orderStateStatsLoc(Droid, DORDER_BUILD,&Stats,&x,&y)) {	// Moving to build location?
		return true;
	}

	return false;
}


// Get the structure for a structure which a droid is currently building.
//
STRUCTURE *DroidGetBuildStructure(DROID *Droid)
{
	BASE_OBJECT *Structure = NULL;

	if (orderStateObj(Droid, DORDER_BUILD))
	{
		Structure = orderStateObj(Droid, DORDER_HELPBUILD);
	}

	return (STRUCTURE*)Structure;
}

// Get the first factory assigned to a command droid
STRUCTURE *droidGetCommandFactory(DROID *psDroid)
{
	SDWORD		inc;
	STRUCTURE	*psCurr;

	for(inc = 0; inc < MAX_FACTORY; inc++)
	{
		if ( psDroid->secondaryOrder & (1 << (inc + DSS_ASSPROD_SHIFT)) )
		{
			// found an assigned factory - look for it in the lists
			for (psCurr = apsStructLists[psDroid->player]; psCurr; psCurr = psCurr->psNext)
			{
				if ( (psCurr->pStructureType->type == REF_FACTORY) &&
					 ( ((FACTORY *)psCurr->pFunctionality)->
								psAssemblyPoint->factoryInc == inc ) )
				{
					return psCurr;
				}
			}
		}
		if ( psDroid->secondaryOrder & (1 << (inc + DSS_ASSPROD_CYBORG_SHIFT)) )
		{
			// found an assigned factory - look for it in the lists
			for (psCurr = apsStructLists[psDroid->player]; psCurr; psCurr = psCurr->psNext)
			{
				if ( (psCurr->pStructureType->type == REF_CYBORG_FACTORY) &&
					 ( ((FACTORY *)psCurr->pFunctionality)->
								psAssemblyPoint->factoryInc == inc ) )
				{
					return psCurr;
				}
			}
		}
		if ( psDroid->secondaryOrder & (1 << (inc + DSS_ASSPROD_VTOL_SHIFT)) )
		{
			// found an assigned factory - look for it in the lists
			for (psCurr = apsStructLists[psDroid->player]; psCurr; psCurr = psCurr->psNext)
			{
				if ( (psCurr->pStructureType->type == REF_VTOL_FACTORY) &&
					 ( ((FACTORY *)psCurr->pFunctionality)->
								psAssemblyPoint->factoryInc == inc ) )
				{
					return psCurr;
				}
			}
		}
	}

	return NULL;
}

// Get the stats for a structure which a droid is going to ( but not yet ) building.
//
BASE_STATS *DroidGetBuildStats(DROID *Droid)
{
	BASE_STATS *Stats;
	UDWORD x,y;

	if(orderStateStatsLoc(Droid, DORDER_BUILD,&Stats,&x,&y)) {	// Moving to build location?
		return Stats;
	}

	return NULL;
}

iIMDShape *DroidGetIMD(DROID *Droid)
{
	return Droid->sDisplay.imd;
}

/*UDWORD DroidGetIMDIndex(DROID *Droid)
{
	return Droid->imdNum;
}*/

BOOL StructureIsManufacturing(STRUCTURE *Structure)
{
	return ((Structure->pStructureType->type == REF_FACTORY ||
			Structure->pStructureType->type == REF_CYBORG_FACTORY ||
			Structure->pStructureType->type == REF_VTOL_FACTORY) &&
			((FACTORY*)Structure->pFunctionality)->psSubject);
}

FACTORY *StructureGetFactory(STRUCTURE *Structure)
{
	return (FACTORY*)Structure->pFunctionality;
}

BOOL StructureIsResearching(STRUCTURE *Structure)
{
	return (Structure->pStructureType->type == REF_RESEARCH) &&
			((RESEARCH_FACILITY*)Structure->pFunctionality)->psSubject;
}

RESEARCH_FACILITY *StructureGetResearch(STRUCTURE *Structure)
{
	return (RESEARCH_FACILITY*)Structure->pFunctionality;
}


iIMDShape *StructureGetIMD(STRUCTURE *Structure)
{
	return Structure->pStructureType->pIMD;
}


DROID_TEMPLATE *FactoryGetTemplate(FACTORY *Factory)
{
	return (DROID_TEMPLATE*)Factory->psSubject;
}

BOOL StatIsStructure(BASE_STATS *Stat)
{
	return (Stat->ref >= REF_STRUCTURE_START && Stat->ref <
				REF_STRUCTURE_START + REF_RANGE);
}

BOOL StatIsFeature(BASE_STATS *Stat)
{
	return (Stat->ref >= REF_FEATURE_START && Stat->ref <
				REF_FEATURE_START + REF_RANGE);
}

iIMDShape *StatGetStructureIMD(BASE_STATS *Stat,UDWORD Player)
{
	(void)Player;
	//return buildingIMDs[aBuildingIMDs[Player][((STRUCTURE_STATS*)Stat)->type]];
	return ((STRUCTURE_STATS*)Stat)->pIMD;
}

BOOL StatIsTemplate(BASE_STATS *Stat)
{
	return (Stat->ref >= REF_TEMPLATE_START &&
				 Stat->ref < REF_TEMPLATE_START + REF_RANGE);
}

SDWORD StatIsComponent(BASE_STATS *Stat)
{
	if(Stat->ref >= REF_BODY_START &&
				 Stat->ref < REF_BODY_START + REF_RANGE) {
		//return true;
		return COMP_BODY;
	}

	if(Stat->ref >= REF_BRAIN_START &&
				 Stat->ref < REF_BRAIN_START + REF_RANGE) {
		//return true;
		return COMP_BRAIN;
	}

	if(Stat->ref >= REF_PROPULSION_START &&
				 Stat->ref < REF_PROPULSION_START + REF_RANGE) {
		//return true;
		return COMP_PROPULSION;
	}

	if(Stat->ref >= REF_WEAPON_START &&
				 Stat->ref < REF_WEAPON_START + REF_RANGE) {
		//return true;
		return COMP_WEAPON;
	}

	if(Stat->ref >= REF_SENSOR_START &&
				 Stat->ref < REF_SENSOR_START + REF_RANGE) {
		//return true;
		return COMP_SENSOR;
	}

	if(Stat->ref >= REF_ECM_START &&
				 Stat->ref < REF_ECM_START + REF_RANGE) {
		//return true;
		return COMP_ECM;
	}

	if(Stat->ref >= REF_CONSTRUCT_START &&
				 Stat->ref < REF_CONSTRUCT_START + REF_RANGE) {
		//return true;
		return COMP_CONSTRUCT;
	}

	if(Stat->ref >= REF_REPAIR_START &&
				 Stat->ref < REF_REPAIR_START + REF_RANGE) {
		//return true;
		return COMP_REPAIRUNIT;
	}

	//return false;
	return COMP_UNKNOWN;
}

BOOL StatGetComponentIMD(BASE_STATS *Stat, SDWORD compID,iIMDShape **CompIMD,iIMDShape **MountIMD)
{
	WEAPON_STATS		*psWStat;

	*CompIMD = NULL;
	*MountIMD = NULL;

	switch (compID)
	{
	case COMP_BODY:
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;
//		return ((COMP_BASE_STATS *)Stat)->pIMD;

	case COMP_BRAIN:
//		ASSERT( ((UBYTE*)Stat >= (UBYTE*)asCommandDroids) &&
//				 ((UBYTE*)Stat < (UBYTE*)asCommandDroids + sizeof(asCommandDroids)),
//				 "StatGetComponentIMD: This 'BRAIN_STATS' is actually meant to be a 'COMMAND_DROID'" );

//		psWStat = asWeaponStats + ((COMMAND_DROID *)Stat)->nWeapStat;
		psWStat = ((BRAIN_STATS *)Stat)->psWeaponStat;
		*MountIMD = psWStat->pMountGraphic;
		*CompIMD = psWStat->pIMD;
		return true;

	case COMP_WEAPON:
		*MountIMD = ((WEAPON_STATS*)Stat)->pMountGraphic;
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;

	case COMP_SENSOR:
		*MountIMD = ((SENSOR_STATS*)Stat)->pMountGraphic;
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;

	case COMP_ECM:
		*MountIMD = ((ECM_STATS*)Stat)->pMountGraphic;
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;

	case COMP_CONSTRUCT:
		*MountIMD = ((CONSTRUCT_STATS*)Stat)->pMountGraphic;
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;

	case COMP_PROPULSION:
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;

	case COMP_REPAIRUNIT:
		*MountIMD = ((REPAIR_STATS*)Stat)->pMountGraphic;
		*CompIMD = ((COMP_BASE_STATS *)Stat)->pIMD;
		return true;

	default:
		//COMP_UNKNOWN should be an error
		ASSERT( false, "StatGetComponent : Unknown component" );
	}

	return false;
}


BOOL StatIsResearch(BASE_STATS *Stat)
{
	return (Stat->ref >= REF_RESEARCH_START && Stat->ref <
				REF_RESEARCH_START + REF_RANGE);
}

//void StatGetResearchImage(BASE_STATS *psStat, SDWORD *Image,iIMDShape **Shape, BOOL drawTechIcon)
void StatGetResearchImage(BASE_STATS *psStat, SDWORD *Image, iIMDShape **Shape,
                          BASE_STATS **ppGraphicData, BOOL drawTechIcon)
{
	*Image = -1;
	if (drawTechIcon)
	{
		if (((RESEARCH *)psStat)->iconID != NO_RESEARCH_ICON)
		{
			*Image = ((RESEARCH *)psStat)->iconID;
		}
	}
    //if the research has a Stat associated with it - use this as display in the button
    if (((RESEARCH *)psStat)->psStat)
    {
        *ppGraphicData = ((RESEARCH *)psStat)->psStat;
        //make sure the IMDShape is initialised
        *Shape = NULL;
    }
    else
    {
        //no stat so just just the IMD associated with the research
	    *Shape = ((RESEARCH *)psStat)->pIMD;
        //make sure the stat is initialised
        *ppGraphicData = NULL;
    }
}

/* Draws a stats bar for the design screen */
void intDisplayStatsBar(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_BARGRAPH		*BarGraph = (W_BARGRAPH*)psWidget;
	SDWORD			x0, y0, iX, iY;
	static char		szVal[6], szCheckWidth[6] = "00000";

	x0 = xOffset + BarGraph->x;
	y0 = yOffset + BarGraph->y;

//	//draw the background image
//	iV_DrawImage(IntImages,IMAGE_DES_STATSBACK,x0,y0);

	//increment for the position of the level indicator
	x0 += 3;
	y0 += 3;

	/* indent to allow text value */
	iX = x0 + iV_GetTextWidth( szCheckWidth );
	iY = y0 + (iV_GetImageHeight(IntImages,IMAGE_DES_STATSCURR) - iV_GetTextLineSize())/2 -
					iV_GetTextAboveBase();

	//draw current value section
	iV_DrawImageRect( IntImages, IMAGE_DES_STATSCURR, iX, y0,
			BarGraph->majorSize, iV_GetImageHeight(IntImages,IMAGE_DES_STATSCURR));

	/* draw text value */
	sprintf( szVal, "%d", BarGraph->iValue );
	iV_SetTextColour(WZCOL_TEXT_BRIGHT);
	iV_DrawText( szVal, x0, iY );

	//draw the comparison value - only if not zero
	if (BarGraph->minorSize != 0)
	{
		y0 -= 1;
		iV_DrawImage(IntImages,IMAGE_DES_STATSCOMP,iX+BarGraph->minorSize ,y0);
	}
}



/* Draws a Template Power Bar for the Design Screen */
void intDisplayDesignPowerBar(WIDGET *psWidget, UDWORD xOffset,
							  UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_BARGRAPH      *BarGraph = (W_BARGRAPH*)psWidget;
	SDWORD		    x0, y0, iX, iY;
    UDWORD          width, barWidth;
	static char		szVal[6], szCheckWidth[6] = "00000";
    UBYTE           arbitaryOffset;

	x0 = xOffset + BarGraph->x;
	y0 = yOffset + BarGraph->y;

	//draw the background image
	iV_DrawImage(IntImages,IMAGE_DES_POWERBAR_LEFT,x0,y0);
	iV_DrawImage(IntImages,IMAGE_DES_POWERBAR_RIGHT,
		//xOffset+psWidget->width-iV_GetImageWidth(IntImages, IMAGE_DES_POWERBAR_RIGHT),y0);
        x0 + psWidget->width-iV_GetImageWidth(IntImages, IMAGE_DES_POWERBAR_RIGHT),y0);

	//increment for the position of the bars within the background image
    arbitaryOffset = 3;
	x0 += arbitaryOffset;
	y0 += arbitaryOffset;

	/* indent to allow text value */
	iX = x0 + iV_GetTextWidth( szCheckWidth );
	iY = y0 + (iV_GetImageHeight(IntImages,IMAGE_DES_STATSCURR) - iV_GetTextLineSize())/2 -
					iV_GetTextAboveBase();

    //adjust the width based on the text drawn
    barWidth = BarGraph->width - (iX - x0 + arbitaryOffset);
    width = BarGraph->majorSize * barWidth / 100;
    //quick check that don't go over the end - ensure % is not > 100
	if (width > barWidth)
	{
		width = barWidth;
	}

	//draw current value section
	iV_DrawImageRect(IntImages,IMAGE_DES_STATSCURR, iX, y0,
                        width, iV_GetImageHeight(IntImages,IMAGE_DES_STATSCURR));

	/* draw text value */
	sprintf( szVal, "%d", BarGraph->iValue );
	iV_SetTextColour(WZCOL_TEXT_BRIGHT);
	iV_DrawText( szVal, x0, iY );

	//draw the comparison value - only if not zero
	if (BarGraph->minorSize != 0)
	{
		y0 -= 1;
        width = BarGraph->minorSize * barWidth / 100;
        if (width > barWidth)
        {
            width = barWidth;
        }
		//iV_DrawImage(IntImages,IMAGE_DES_STATSCOMP,x0+BarGraph->minorSize ,y0);
        iV_DrawImage(IntImages, IMAGE_DES_STATSCOMP, iX + width ,y0);
	}

}



// Widget callback function to play an audio track.
//
#define WIDGETBEEPGAP (200)	// 200 milliseconds between each beep please
void WidgetAudioCallback(int AudioID)
{
	static	SDWORD LastTimeAudio;
	if(AudioID >= 0) {

		SDWORD TimeSinceLastWidgetBeep;

		// Don't allow a widget beep if one was made in the last WIDGETBEEPGAP milliseconds
		// This stops double beeps happening (which seems to happen all the time)
		TimeSinceLastWidgetBeep=gameTime2-LastTimeAudio;
		if (TimeSinceLastWidgetBeep<0 || TimeSinceLastWidgetBeep>WIDGETBEEPGAP)
		{
			LastTimeAudio=gameTime2;
			audio_PlayTrack(AudioID);
		}
	}
}


// Widget callback to display a contents button for the Transporter
void intDisplayTransportButton(WIDGET *psWidget, UDWORD xOffset,
						  UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_CLICKFORM			*Form = (W_CLICKFORM*)psWidget;
	BOOL				Down;
	BOOL				Hilight = false;
	RENDERED_BUTTON		*Buffer = (RENDERED_BUTTON*)Form->pUserData;
	DROID				*psDroid = NULL;
    UDWORD              gfxId;

	OpenButtonRender((UWORD)(xOffset+Form->x), (UWORD)(yOffset+Form->y),(UWORD)Form->width,
		(UWORD)Form->height);

	Down = Form->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK);

    //allocate this outside of the if so the rank icons are always draw
    psDroid = (DROID*)Buffer->Data;
    //there should always be a droid associated with the button
	ASSERT( psDroid != NULL,
		"intDisplayTransportButton: invalid droid pointer" );

	{
		Hilight = Form->state & WCLICK_HILITE;

		if(Hilight)
		{
			Buffer->ImdRotation += timeAdjustedIncrement(BUTTONOBJ_ROTSPEED, false);
		}

		Hilight = formIsHilite(Form);

		Buffer->State = Form->state;

		//psDroid = (DROID*)Buffer->Data;

		//there should always be a droid associated with the button
		//ASSERT( psDroid != NULL,
		//	"intDisplayTransportButton: invalid droid pointer" );

		if (psDroid)
		{
			RenderToButton(NULL,0,psDroid,psDroid->player,Buffer,Down,IMDTYPE_DROID,TOPBUTTON);
		}
		else
		{
			RenderBlankToButton(Buffer,Down,TOPBUTTON);
		}
		RENDERBUTTON_INITIALISED(Buffer);
	}

	CloseButtonRender();

	if (Hilight)
	{
		iV_DrawImage(IntImages,IMAGE_BUT_HILITE,xOffset+Form->x,yOffset+Form->y);
	}

    //if (psDroid && missionIsOffworld()) Want this on all reInforcement missions
    if (psDroid && missionForReInforcements())
    {
        //add the experience level for each droid
        gfxId = getDroidRankGraphic(psDroid);
	    if(gfxId != UDWORD_MAX)
	    {
		    /* Render the rank graphic at the correct location */
		    /* Render the rank graphic at the correct location */
		    iV_DrawImage(IntImages,(UWORD)gfxId,xOffset+Form->x+50,yOffset+Form->y+30);

	    }
    }
}


/* Draws blips on radar to represent Proximity Display and damaged structures */
void drawRadarBlips(float pixSizeH, float pixSizeV, int RadarOffsetX, int RadarOffsetY)
{
	PROXIMITY_DISPLAY	*psProxDisp;
	UWORD			imageID;
	UDWORD			delay = 150;
	UDWORD			i;

	// Check if it's time to remove beacons
	for (i = 0; i < MAX_PLAYERS; i++)
	{
		/* Go through all the proximity Displays*/
		for (psProxDisp = apsProxDisp[i]; psProxDisp != NULL; psProxDisp = psProxDisp->psNext)
		{
			if(psProxDisp->psMessage->dataType == MSG_DATA_BEACON)
			{
				MESSAGE		*psCurrMsg = psProxDisp->psMessage;
				VIEWDATA	*pViewData = (VIEWDATA *)psCurrMsg->pViewData;

				ASSERT(pViewData != NULL, "Message without data!");

				if (pViewData->type == VIEW_BEACON)
				{
					if (pViewData->pData == NULL)
					{
						debug(LOG_ERROR, "Found a help message without data!");
					}
					else if ((((VIEW_PROXIMITY *)pViewData->pData)->timeAdded + 60000) <= gameTime)
					{
						debug(LOG_MSG, "blip timeout for %d, from %d", i, (((VIEW_PROXIMITY *)pViewData->pData)->sender));
						removeMessage(psCurrMsg, i);	//remove beacon
						break;	//there can only be 1 beacon per player
					}
				}
			}
		}
	}

	/* Go through all the proximity Displays */
	for (psProxDisp = apsProxDisp[selectedPlayer]; psProxDisp != NULL; psProxDisp = psProxDisp->psNext)
	{
		PROX_TYPE	proxType;

		if (psProxDisp->type == POS_PROXDATA)
		{
			proxType = ((VIEW_PROXIMITY*)((VIEWDATA *)psProxDisp->psMessage->pViewData)->pData)->proxType;
		}
		else
		{
			FEATURE *psFeature = (FEATURE *)psProxDisp->psMessage->pViewData;

			ASSERT(psFeature && psFeature->psStats, "Bad feature message")
			if (psFeature && psFeature->psStats && psFeature->psStats->subType == FEAT_OIL_RESOURCE)
			{
				proxType = PROX_RESOURCE;
			}
			else
			{
				proxType = PROX_ARTEFACT;
			}
		}

		// Draw the 'blips' on the radar - use same timings as radar blips if the message is read - don't animate
		if (psProxDisp->psMessage->read)
		{
			imageID = IMAGE_RAD_ENM3 + (proxType * (NUM_PULSES + 1));
		}
		else
		{
			// Draw animated
			if ((gameTime2 - psProxDisp->timeLastDrawn) > delay)
			{
				psProxDisp->strobe++;
				if (psProxDisp->strobe > (NUM_PULSES-1))
				{
					psProxDisp->strobe = 0;
				}
				psProxDisp->timeLastDrawn = gameTime2;
			}
			imageID = (UWORD)(IMAGE_RAD_ENM1 + psProxDisp->strobe + (proxType * (NUM_PULSES + 1)));
		}
		// Draw the 'blip'
		iV_DrawImage(IntImages, imageID, psProxDisp->radarX + RADTLX, psProxDisp->radarY + RADTLY);
	}
}


/*Displays the proximity messages blips over the world*/
void intDisplayProximityBlips(WIDGET *psWidget, WZ_DECL_UNUSED UDWORD xOffset,
					WZ_DECL_UNUSED UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_CLICKFORM			*psButton = (W_CLICKFORM*)psWidget;
	PROXIMITY_DISPLAY	*psProxDisp = (PROXIMITY_DISPLAY *)psButton->pUserData;
	MESSAGE				*psMsg = psProxDisp->psMessage;
	SDWORD				x = 0, y = 0;

	ASSERT( psMsg->type == MSG_PROXIMITY, "Invalid message type" );

	//if no data - ignore message
	if (psMsg->pViewData == NULL)
	{
		return;
	}
	if (psProxDisp->type == POS_PROXDATA)
	{
		x = ((VIEW_PROXIMITY*)((VIEWDATA *)psProxDisp->psMessage->pViewData)->pData)->x;
		y = ((VIEW_PROXIMITY*)((VIEWDATA *)psProxDisp->psMessage->pViewData)->pData)->y;
	}
	else if (psProxDisp->type == POS_PROXOBJ)
	{
		x = ((BASE_OBJECT *)psProxDisp->psMessage->pViewData)->pos.x;
		y = ((BASE_OBJECT *)psProxDisp->psMessage->pViewData)->pos.y;
	}

	//if not within view ignore message
	if (!clipXY(x, y))
	{
		return;
	}

	//if the message is read - don't draw
	if (!psMsg->read)
	{
		//set the button's x/y so that can be clicked on
		psButton->x = (SWORD)(psProxDisp->screenX - psButton->width/2);
		psButton->y = (SWORD)(psProxDisp->screenY - psButton->height/2);
	}
}


static UDWORD sliderMousePos(	W_SLIDER *Slider )
{
	return (widgGetFromID(psWScreen,Slider->formID)->x + Slider->x)
			+ ((Slider->pos * Slider->width) / Slider->numStops );
}


static UWORD sliderMouseUnit(W_SLIDER *Slider)
{
	UWORD posStops = (UWORD)(Slider->numStops / 20);

	if(posStops==0 || Slider->pos == 0 || Slider->pos == Slider->numStops)
	{
		return 1;
	}

	if(Slider->pos < posStops)
	{
		return (Slider->pos);
	}

	if(Slider->pos > (Slider->numStops-posStops))
	{
		return (UWORD)(Slider->numStops-Slider->pos);
	}
	return posStops;
}

void intUpdateQuantitySlider(WIDGET *psWidget, W_CONTEXT *psContext)
{
	W_SLIDER *Slider = (W_SLIDER*)psWidget;

	if(Slider->state & SLD_HILITE)
	{
		if(keyDown(KEY_LEFTARROW))
		{
			if(Slider->pos > 0)
			{
				Slider->pos = (UWORD)(Slider->pos - sliderMouseUnit(Slider));
				bUsingSlider = true;
				SetMousePos(sliderMousePos(Slider), mouseY());	// move mouse
			}
		}
		else if(keyDown(KEY_RIGHTARROW))
		{
			if(Slider->pos < Slider->numStops)
			{
				Slider->pos = (UWORD)(Slider->pos + sliderMouseUnit(Slider));
				bUsingSlider = true;
				SetMousePos(sliderMousePos(Slider), mouseY());	// move mouse
			}
		}
	}
}

void intUpdateOptionText(WIDGET *psWidget, W_CONTEXT *psContext)
{
}



void intDisplayResSubGroup(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_LABEL		*Label = (W_LABEL*)psWidget;
	UDWORD		x = Label->x + xOffset;
	UDWORD		y = Label->y + yOffset;
	RESEARCH    *psResearch = (RESEARCH *)Label->pUserData;

    if (psResearch->subGroup != NO_RESEARCH_ICON)
    {
	    iV_DrawImage(IntImages,psResearch->subGroup,x,y);
    }
}

void intDisplayAllyIcon(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
	W_LABEL		*Label =  (W_LABEL*)psWidget;
	UDWORD		x = Label->x + xOffset;
	UDWORD		y = Label->y + yOffset;

	iV_DrawImage(IntImages, IMAGE_DES_BODYPOINTS, x, y);
}
